! ************************************************************************************************** ! ____________________ ___ ________ __ ! / ____/ _/_ __/ __ \/ | / ____/ //_/ ! / /_ / / / / / /_/ / /| |/ / / ,< ! / __/ _/ / / / / ____/ ___ / /___/ /| | ! /_/ /___/ /_/ /_/ /_/ |_\____/_/ |_| ! ! A Curve Fitting Package ! ! Refactored by Federico Perini, 10/6/2022 ! Based on the netlib library by Paul Dierckx ! ! References : ! - C. De Boor, "On calculating with b-splines", J Approx Theory 6 (1972) 50-62 ! - M. G. Cox, "The numerical evaluation of b-splines", J Inst Maths Applics 10 (1972) 134-149 ! - P. Dierckx, "Curve and surface fitting with splines", Monographs on numerical analysis, ! Oxford university press, 1993. ! ! ************************************************************************************************** module fitpack_core use iso_fortran_env, only: real64,int32 implicit none private ! Precision and array size integer, parameter, public :: RKIND = real64 integer, parameter, public :: RSIZE = int32 ! Curve fitting routines public :: curfit ! General curve fitting public :: percur ! Curve fitting with periodic splines public :: parcur ! Smoothing of parametric curves public :: clocur ! Smoothing of closed curves public :: concur ! Smoothing with end-point derivative constraints public :: cocosp ! Least-squares fitting with convexity constraints public :: concon ! Automated smoothing with convexity constraints ! Curve approximation routines public :: splev ! Evaluation of a spline function public :: splder ! Derivative calculation of a spline function public :: spalde ! All derivateives of a spline function public :: curev ! Evaluation of a spline curve public :: cualde ! All derivatives of a spline curve public :: insert ! Insert a knot into a given spline public :: insert_inplace public :: splint ! Integration of a spline function public :: fourco ! Fourier coefficients of a cubic spline public :: sproot ! The roots of a cubic spline ! Surface fitting routines public :: surfit ! Surface fitting to scattered data public :: regrid ! Surface fitting to data on a rectangular grid public :: polar ! Surface fitting using generalized polar coordinates public :: pogrid ! Surface fitting to data on a polar grid public :: sphere ! Surface fitting using spherical coordinates public :: spgrid ! Surface fitting to data on a spherical grid public :: parsur ! Parametric surface fitting to data on a grid ! Surface application routines public :: bispeu ! Evaluation of a bivariate spline function public :: bispev ! Evaluation of a bivariate spline function public :: parder ! Partial derivatives of a bivariate spline public :: pardeu ! Partial derivatives of a bivariate spline public :: pardtc ! Create partial derivative splane of a bivariate spline public :: dblint ! Integration of a bivariate spline public :: profil ! Cross-section of a bivariate spline public :: evapol ! Evaluation of a polar spline public :: surev ! Evaluation of a parametric spline surface ! Spline behavior for points not in the support integer, parameter, public :: OUTSIDE_EXTRAPOLATE = 0 ! extrapolated from the end spans integer, parameter, public :: OUTSIDE_ZERO = 1 ! spline evaluates to zero integer, parameter, public :: OUTSIDE_NOT_ALLOWED = 2 ! an error flag is returned integer, parameter, public :: OUTSIDE_NEAREST_BND = 3 ! evaluate to value of nearest boundary point ! Dimension of last knot addition integer, parameter, public :: KNOT_DIM_NONE = 0 ! No knots added yet integer, parameter, public :: KNOT_DIM_2 = 1 ! Last knot added on 2nd dim (y or v) integer, parameter, public :: KNOT_DIM_1 = -1 ! Last knot added on 1st dim (x or u) public :: FITPACK_MESSAGE public :: FITPACK_SUCCESS integer, parameter, public :: FITPACK_OK = 0 ! ok for spline, abs(fp-s)/s <= tol=0.001 integer, parameter, public :: FITPACK_INTERPOLATING_OK = -1 ! ok for interpolating spline, fp=0 integer, parameter, public :: FITPACK_LEASTSQUARES_OK = -2 ! ok for weighted least-squares polynomial of degree k. integer, parameter, public :: FITPACK_INSUFFICIENT_STORAGE = 1 integer, parameter, public :: FITPACK_S_TOO_SMALL = 2 integer, parameter, public :: FITPACK_MAXIT = 3 integer, parameter, public :: FITPACK_TOO_MANY_KNOTS = 4 integer, parameter, public :: FITPACK_OVERLAPPING_KNOTS = 5 integer, parameter, public :: FITPACK_INVALID_RANGE = 6 integer, parameter, public :: FITPACK_INPUT_ERROR = 10 integer, parameter, public :: FITPACK_TEST_ERROR = 11 ! Internal Parameters integer , parameter, public :: MAX_IDIM = 10 ! Max number of dimensions integer , parameter, public :: MAX_ORDER = 19 ! Max spline order (for array allocation) real(RKIND), parameter, public :: one = 1.0_RKIND real(RKIND), parameter, public :: zero = 0.0_RKIND real(RKIND), parameter, public :: half = 0.5_RKIND real(RKIND), parameter, public :: onep5 = 1.5_RKIND real(RKIND), parameter, public :: fourth = 0.25_RKIND real(RKIND), parameter, public :: two = 2.0_RKIND real(RKIND), parameter, public :: three = 3.0_RKIND real(RKIND), parameter, public :: four = 4.0_RKIND real(RKIND), parameter, public :: five = 5.0_RKIND real(RKIND), parameter, public :: six = 6.0_RKIND real(RKIND), parameter, public :: ten = 10.0_RKIND real(RKIND), parameter, public :: pi = atan2(zero,-one) real(RKIND), parameter, public :: pi2 = 2*pi real(RKIND), parameter, public :: pi4 = 4*pi real(RKIND), parameter, public :: pio2 = half*pi real(RKIND), parameter, public :: pio4 = fourth*pi real(RKIND), parameter, public :: pio8 = 0.125_RKIND*pi real(RKIND), parameter, public :: deg2rad = pi/180.0_RKIND real(RKIND), parameter, public :: smallnum03 = 1.0e-03_RKIND real(RKIND), parameter, public :: smallnum06 = 1.0e-06_RKIND real(RKIND), parameter, public :: smallnum08 = 1.0e-08_RKIND real(RKIND), parameter, public :: smallnum10 = 1.0e-10_RKIND abstract interface ! Function defining the boundary of the curve approximation domain pure real(RKIND) function boundary(v) result(rad) import RKIND real(RKIND), intent(in) :: v end function boundary end interface contains ! Wrapper for the error flag pure function FITPACK_MESSAGE(ierr) result(msg) integer, intent(in) :: ierr character(len=:), allocatable :: msg select case (ierr) case (FITPACK_OK); msg = 'Success!' case (FITPACK_INTERPOLATING_OK); msg = 'Success! (interpolation)' case (FITPACK_LEASTSQUARES_OK); msg = 'Success! (least-squares)' case (FITPACK_INSUFFICIENT_STORAGE); msg = 'Insufficient Storage' case (FITPACK_S_TOO_SMALL); msg = 'Smoothing parameter is too small' case (FITPACK_MAXIT); msg = 'Infinite loop detected' case (FITPACK_TOO_MANY_KNOTS); msg = 'More knots than data points' case (FITPACK_OVERLAPPING_KNOTS); msg = 'Overlapping knots found' case (FITPACK_INVALID_RANGE); msg = 'Invalid variable range' case (FITPACK_INPUT_ERROR); msg = 'Invalid input' case (FITPACK_TEST_ERROR); msg = 'Test(s) failed' case default; msg = 'UNKNOWN ERROR' end select end function FITPACK_MESSAGE ! Wrapper for OK elemental logical function FITPACK_SUCCESS(ierr) integer, intent(in) :: ierr FITPACK_SUCCESS = ierr<=FITPACK_OK end function FITPACK_SUCCESS pure subroutine bispeu(tx,nx,ty,ny,c,kx,ky,x,y,z,m,wrk,lwrk,ier) ! subroutine bispeu evaluates on a set of points (x(i),y(i)),i=1,...,m ! a bivariate spline s(x,y) of degrees kx and ky, given in the ! b-spline representation. ! ! calling sequence: ! call bispeu(tx,nx,ty,ny,c,kx,ky,x,y,z,m,wrk,lwrk,iwrk,kwrk,ier) ! ! input parameters: ! tx : real array, length nx, which contains the position of the knots in the x-direction. ! nx : integer, giving the total number of knots in the x-direction ! ty : real array, length ny, which contains the position of the knots in the y-direction. ! ny : integer, giving the total number of knots in the y-direction ! c : real array, length (nx-kx-1)*(ny-ky-1), which contains the b-spline coefficients. ! kx,ky : integer values, giving the degrees of the spline. ! x : real array of dimension (mx). ! y : real array of dimension (my). ! m : on entry m must specify the number points. m >= 1. ! wrk : real array of dimension lwrk. used as workspace. ! lwrk : integer, specifying the dimension of wrk. lwrk >= kx+ky+2 ! ! output parameters: ! z : real array of dimension m. ! on successful exit z(i) contains the value of s(x,y) ! at the point (x(i),y(i)), i=1,...,m. ! ier : integer error flag ! ! restrictions: ! m >=1, lwrk>=mx*(kx+1)+my*(ky+1), kwrk>=mx+my ! tx(kx+1) <= x(i-1) <= x(i) <= tx(nx-kx), i=2,...,mx ! ty(ky+1) <= y(j-1) <= y(j) <= ty(ny-ky), j=2,...,my ! ! other subroutines required: ! fpbisp,fpbspl ! ! ..scalar arguments.. integer, intent(in) :: nx,ny,kx,ky,m,lwrk integer, intent(out) :: ier ! ..array arguments.. real(RKIND), intent(in) :: tx(nx),ty(ny),c((nx-kx-1)*(ny-ky-1)),x(m),y(m) real(RKIND), intent(inout) :: wrk(lwrk) real(RKIND), intent(out) :: z(m) ! ..local scalars.. integer :: iwrk(2),i,lwest ! Check inputs lwest = kx+ky+2 if (lwrk<lwest .or. m<1) then ier = FITPACK_INPUT_ERROR return else ier = FITPACK_OK do i=1,m call fpbisp(tx,nx,ty,ny,c,kx,ky,x(i),1,y(i),1,z(i),wrk(1),wrk(kx+2),iwrk(1),iwrk(2)) end do end if end subroutine bispeu ! subroutine bispev evaluates on a grid (x(i),y(j)),i=1,...,mx; j=1,...,my a bivariate spline ! s(x,y) of degrees kx and ky, given in the b-spline representation. pure subroutine bispev(tx,nx,ty,ny,c,kx,ky,x,mx,y,my,z,wrk,lwrk,iwrk,kwrk,ier) ! ! input parameters: ! tx : real array, length nx, which contains the position of the knots in the x-direction. ! nx : integer, giving the total number of knots in the x-direction ! ty : real array, length ny, which contains the position of the knots in the y-direction. ! ny : integer, giving the total number of knots in the y-direction ! c : real array, length (nx-kx-1)*(ny-ky-1), which contains the b-spline coefficients. ! kx,ky : integer values, giving the degrees of the spline. ! x : real array of dimension (mx). ! before entry x(i) must be set to the x co-ordinate of the i-th grid point along the x-axis. ! tx(kx+1)<=x(i-1)<=x(i)<=tx(nx-kx), i=2,...,mx. ! mx : on entry mx must specify the number of grid points along the x-axis. mx >=1. ! y : real array of dimension (my). ! before entry y(j) must be set to the y co-ordinate of the j-th grid point along the y-axis. ! ty(ky+1)<=y(j-1)<=y(j)<=ty(ny-ky), j=2,...,my. ! my : on entry my must specify the number of grid points along the y-axis. my >=1. ! wrk : real array of dimension lwrk. used as workspace. ! lwrk : integer, specifying the dimension of wrk. ! lwrk >= mx*(kx+1)+my*(ky+1) ! iwrk : integer array of dimension kwrk. used as workspace. ! kwrk : integer, specifying the dimension of iwrk. kwrk >= mx+my. ! ! output parameters: ! z : real array of dimension (mx*my). ! on successful exit z(my*(i-1)+j) contains the value of s(x,y) ! at the point (x(i),y(j)),i=1,...,mx;j=1,...,my. ! ier : integer error flag ! ! restrictions: ! mx >=1, my >=1, lwrk>=mx*(kx+1)+my*(ky+1), kwrk>=mx+my ! tx(kx+1) <= x(i-1) <= x(i) <= tx(nx-kx), i=2,...,mx ! ty(ky+1) <= y(j-1) <= y(j) <= ty(ny-ky), j=2,...,my ! ! other subroutines required: ! fpbisp,fpbspl ! ! references : ! de boor c : on calculating with b-splines, j. approximation theory ! 6 (1972) 50-62. ! cox m.g. : the numerical evaluation of b-splines, j. inst. maths ! applics 10 (1972) 134-149. ! dierckx p. : curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! latest update : march 1987 ! ! ..scalar arguments.. integer, intent(in) :: nx,ny,kx,ky,mx,my,lwrk,kwrk integer, intent(out) :: ier ! ..array arguments.. integer, intent(inout) :: iwrk(kwrk) real(RKIND), intent(in) :: tx(nx),ty(ny),c((nx-kx-1)*(ny-ky-1)),x(mx),y(my) real(RKIND), intent(out) :: z(mx*my) real(RKIND), intent(inout) :: wrk(lwrk) ! ..local scalars.. integer :: iw,lwest ! .. ! before starting computations a data check is made. if the input data ! are invalid control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR lwest = (kx+1)*mx+(ky+1)*my if (lwrk<lwest .or. kwrk<(mx+my) .or. mx<1 .or. my<1) return if (mx>1 .and. any(x(2:mx)<x(1:mx-1))) return if (my>1 .and. any(y(2:my)<y(1:my-1))) return ! Evaluate spline ier = FITPACK_OK iw = mx*(kx+1)+1 call fpbisp(tx,nx,ty,ny,c,kx,ky,x,mx,y,my,z,wrk(1),wrk(iw),iwrk(1),iwrk(mx+1)) end subroutine bispev ! given the ordered set of m points x(i) in the idim-dimensional space with x(1)=x(m), and given ! also a corresponding set of strictly increasing values u(i) and the set of positive numbers ! w(i),i=1,2,...,m subroutine clocur determines a smooth approximating closed spline ! curve s(u), i.e. ! x1 = s1(u) ! x2 = s2(u) u(1) <= u <= u(m) ! ......... ! xidim = sidim(u) ! with sj(u),j=1,2,...,idim periodic spline functions of degree k with common knots ! t(j),j=1,2,...,n. ! if ipar=1 the values u(i),i=1,2,...,m must be supplied by the user. ! if ipar=0 these values are chosen automatically by clocur as ! v(1) = 0 ! v(i) = v(i-1) + dist(x(i),x(i-1)) ,i=2,3,...,m ! u(i) = v(i)/v(m) ,i=1,2,...,m ! if iopt=-1 clocur calculates the weighted least-squares closed spline curve according to a ! given set of knots. ! if iopt>=0 the number of knots of the splines sj(u) and the position t(j),j=1,2,...,n is chosen ! automatically by the routine. the smoothness of s(u) is then achieved by minimalizing the ! discontinuity jumps of the k-th derivative of s(u) at the knots t(j),j=k+2,k+3,...,n-k-1. ! the amount of smoothness is determined by the condition that ! f(p)=sum((w(i)*dist(x(i),s(u(i))))**2) be <= s, with s a given nonnegative constant, called ! the smoothing factor. ! the fit s(u) is given in the b-spline representation and can be evaluated by means of ! subroutine curev. pure subroutine clocur(iopt,ipar,idim,m,u,mx,x,w,k,s,nest,n,t,nc,c,fp,wrk,lwrk,iwrk,ier) ! ! calling sequence: ! call clocur(iopt,ipar,idim,m,u,mx,x,w,k,s,nest,n,t,nc,c,fp,wrk,lwrk,iwrk,ier) ! ! parameters: ! iopt : integer flag. on entry iopt must specify whether a weighted least-squares closed ! spline curve (iopt=-1) or a smoothing closed spline curve (iopt=0 or 1) must be ! determined. if iopt=0 the routine will start with an initial set of knots ! t(i)=u(1)+(u(m)-u(1))*(i-k-1),i=1,2,...,2*k+2. if iopt=1 the routine will continue ! with the knots found at the last call. attention: a call with iopt=1 must always be ! immediately preceded by another call with iopt=1 or iopt=zero. ! unchanged on exit. ! ipar : integer flag. on entry ipar must specify whether (ipar=1) the user will supply the ! parameter values u(i),or whether (ipar=0) these values are to be calculated by clocur. ! unchanged on exit. ! idim : integer. on entry idim must specify the dimension of the curve. 0 < idim <= MAX_IDIM. ! unchanged on exit. ! m : integer. on entry m must specify the number of data points. m>1. unchanged on exit. ! u : real array of dimension at least (m). in case ipar=1,before entry, u(i) must be set ! to the i-th value of the parameter variable u for i=1,2,...,m. these values must then ! be supplied in strictly ascending order and will be unchanged on exit. in case ipar=0, ! on exit,the array will contain the values u(i) as determined by clocur. ! mx : integer. on entry mx must specify the actual dimension of the array x as declared in ! the calling (sub)program. mx must not be too small (see x). unchanged on exit. ! x : real array of dimension at least idim*m. ! before entry, x(idim*(i-1)+j) must contain the j-th coordinate of the i-th data point ! for i=1,2,...,m and j=1,2,...,idim. since first and last data point must coincide it ! means that x(j)=x(idim*(m-1)+j),j=1,2,...,idim. unchanged on exit. ! w : real array of dimension at least (m). before entry, w(i) must be set to the i-th value ! in the set of weights. the w(i) must be strictly positive. w(m) is not used. ! unchanged on exit. see also further comments. ! k : integer. on entry k must specify the degree of the splines. 1<=k<=5. it is recommended ! to use cubic splines (k=3). the user is strongly dissuaded from choosing k even, ! together with a small s-value. unchanged on exit. ! s : real.on entry (in case iopt>=0) s must specify the smoothing factor. s >=zero ! unchanged on exit. for advice on the choice of s see further comments. ! nest : integer. on entry nest must contain an over-estimate of the total number of knots of ! the splines returned, to indicate the storage space available to the routine. ! nest >=2*k+2. in most practical situation nest=m/2 will be sufficient. always large ! enough is nest=m+2*k, the number of knots needed for interpolation (s=0). ! unchanged on exit. ! n : integer. unless ier = 10 (in case iopt >=0), n will contain the total number of knots ! of the smoothing spline curve returned if the computation mode iopt=1 is used this ! value of n should be left unchanged between subsequent calls. in case iopt=-1, the ! value of n must be specified on entry. ! t : real array of dimension at least (nest). on successful exit, this array will contain ! the knots of the spline curve,i.e. the position of the interior knots t(k+2), ! t(k+3),..,t(n-k-1) as well as the position of the additional t(1),t(2),..,t(k+1)=u(1) ! and u(m)=t(n-k),...,t(n) needed for the b-spline representation. ! if the computation mode iopt=1 is used, the values of t(1),t(2),...,t(n) should be ! left unchanged between subsequent calls. if the computation mode iopt=-1 is used, the ! values t(k+2),...,t(n-k-1) must be supplied by the user, before entry. see also the ! restrictions (ier=10). ! nc : integer. on entry nc must specify the actual dimension of the array c as declared in ! the calling (sub)program. nc must not be too small (see c). unchanged on exit. ! c : real array of dimension at least (nest*idim). on successful exit, this array will ! contain the coefficients in the b-spline representation of the spline curve s(u),i.e. ! the b-spline coefficients of the spline sj(u) will be given in c(n*(j-1)+i),i=1,2,..., ! n-k-1 for j=1,2,...,idim. ! fp : real. unless ier = 10, fp contains the weighted sum of squared residuals of the spline ! curve returned. ! wrk : real array of dimension at least m*(k+1)+nest*(7+idim+5*k). used as working space. ! if the computation mode iopt=1 is used, the values wrk(1),...,wrk(n) should be left ! unchanged between subsequent calls. ! lwrk : integer. on entry,lwrk must specify the actual dimension of the array wrk as declared ! in the calling (sub)program. lwrk must not be too small (see wrk). unchanged on exit. ! iwrk : integer array of dimension at least (nest). used as working space. if the computation ! mode iopt=1 is used,the values iwrk(1),...,iwrk(n) should be left unchanged ! between subsequent calls. ! ier : integer. unless the routine detects an error, ier contains a non-positive value on ! exit, i.e. ! ier=0 : normal return. the close curve returned has a residual sum of squares fp such that ! abs(fp-s)/s <= tol with tol a relative tolerance set to 0.001 by the program. ! ier=-1 : normal return. the curve returned is an interpolating spline curve (fp=0). ! ier=-2 : normal return. the curve returned is the weighted least-squares point,i.e. each ! spline sj(u) is a constant. in this extreme case fp gives the upper bound fp0 for ! the smoothing factor s. ! ier=1 : error. the required storage space exceeds the available storage space, as specified ! by the parameter nest. likely causes : nest too small. if nest is already large (say ! nest > m/2), it may also indicate that s is too small. the approximation returned is ! the least-squares closed curve according to the knots t(1),t(2),...,t(n). (n=nest) ! the parameter fp gives the corresponding weighted sum of squared residuals (fp>s). ! ier=2 : error. a theoretically impossible result was found during the iteration process for ! finding a smoothing curve with fp = s. probably causes : s too small. ! there is an approximation returned but the corresponding weighted sum of squared ! residuals does not satisfy the condition abs(fp-s)/s < tol. ! ier=3 : error. the maximal number of iterations maxit (set to 20 by the program) allowed for ! finding a smoothing curve with fp=s has been reached. probably causes : s too small ! there is an approximation returned but the corresponding weighted sum of squared ! residuals does not satisfy the condition abs(fp-s)/s < tol. ! ier=10 : error. on entry, the input data are controlled on validity the following ! restrictions must be satisfied. ! -1<=iopt<=1, 1<=k<=5, m>1, nest>2*k+2, w(i)>0,i=1,2,...,m ! 0<=ipar<=1, 0<idim<=10, lwrk>=(k+1)*m+nest*(7+idim+5*k), ! nc>=nest*idim, x(j)=x(idim*(m-1)+j), j=1,2,...,idim ! if ipar=0: sum j=1,idim (x(i*idim+j)-x((i-1)*idim+j))**2>0 ! i=1,2,...,m-1. ! if ipar=1: u(1)<u(2)<...<u(m) ! if iopt=-1: 2*k+2<=n<=min(nest,m+2*k) ! u(1)<t(k+2)<t(k+3)<...<t(n-k-1)<u(m) ! (u(1)=0 and u(m)=1 in case ipar=0) ! the schoenberg-whitney conditions, i.e. there ! must be a subset of data points uu(j) with ! uu(j) = u(i) or u(i)+(u(m)-u(1)) such that ! t(j) < uu(j) < t(j+k+1), j=k+1,...,n-k-1 ! if iopt>=0: s>=0 ! if s=0 : nest >= m+2*k ! if one of these conditions is found to be violated,control is immediately repassed ! to the calling program. in that case there is no approximation returned. ! ! further comments: ! by means of the parameter s, the user can control the tradeoff between closeness of fit and ! smoothness of fit of the approximation. if s is too large, the curve will be too smooth and ! signal will be lost ; if s is too small the curve will pick up too much noise. in the extreme ! cases the program will return an interpolating curve if s=0 and the weighted least-squares ! point if s is very large. between these extremes, a properly chosen s will result in a good ! compromise between closeness of fit and smoothness of fit. to decide whether an approximation, ! corresponding to a certain s is satisfactory the user is highly recommended to inspect the ! fits graphically. ! recommended values for s depend on the weights w(i). if these are taken as 1/d(i) with d(i) an ! estimate of the standard deviation of x(i), a good s-value should be found in the range ! (m-sqrt(2*m),m+sqrt(2*m)). if nothing is known about the statistical error in x(i) each w(i) ! can be set equal to one and s determined by trial and error, taking account of the comments ! above. the best is then to start with a very large value of s ( to determine the weighted ! least-squares point and the upper bound fp0 for s) and then to progressively decrease the ! value of s ( say by a factor 10 in the beginning, i.e. s=fp0/10, fp0/100,...and more carefully ! as the approximating curve shows more detail) to obtain closer fits. to economize the search ! for a good s-value the program provides with different modes of computation. at the first call ! of the routine, or whenever he wants to restart with the initial set of knots the user must set ! iopt=zero. ! if iopt=1 the program will continue with the set of knots found at the last call of the ! routine. this will save a lot of computation time if clocur is called repeatedly for different ! values of s. the number of knots of the spline returned and their location will depend on the ! value of s and on the complexity of the shape of the curve underlying the data. but, if the ! computation mode iopt=1 is used, the knots returned may also depend on the s-values at ! previous calls (if these were smaller). therefore, if after a number of trials with different ! s-values and iopt=1, the user can finally accept a fit as satisfactory, it may be worthwhile ! for him to call clocur once more with the selected value for s but now with iopt=zero indeed, ! clocur may then return an approximation of the same quality of fit but with fewer knots and ! therefore better if data reduction is also an important objective for the user. ! ! the form of the approximating curve can strongly be affected by the choice of the parameter ! values u(i). if there is no physical reason for choosing a particular parameter u, often good ! results will be obtained with the choice of clocur(in case ipar=0), i.e. ! v(1)=0, v(i)=v(i-1)+q(i), i=2,...,m, u(i)=v(i)/v(m), i=1,..,m ! where ! q(i)= sqrt(sum j=1,idim (xj(i)-xj(i-1))**2 ) ! other possibilities for q(i) are ! q(i)= sum j=1,idim (xj(i)-xj(i-1))**2 ! q(i)= sum j=1,idim abs(xj(i)-xj(i-1)) ! q(i)= max j=1,idim abs(xj(i)-xj(i-1)) ! q(i)= 1 ! ! ! other subroutines required: ! fpbacp,fpbspl,fpchep,fpclos,fpdisc,fpgivs,fpknot,fprati,fprota ! ! references: ! dierckx p. : algorithms for smoothing data with periodic and parametric splines, ! computer graphics and image processing 20 (1982) 171-184. ! dierckx p. : algorithms for smoothing data with periodic and parametric splines, ! report tw55, dept. computer science, k.u.leuven, 1981. ! dierckx p. : curve and surface fitting with splines, ! monographs on numerical analysis, oxford university press, 1993. ! ! author: ! p.dierckx ! dept. computer science, k.u. leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! creation date : may 1979 ! ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: s real(RKIND), intent(inout) :: fp integer, intent(in) :: iopt,ipar,idim,m,mx,k,nest,nc,lwrk integer, intent(inout) :: n,ier ! ..array arguments.. real(RKIND), intent(in) :: x(mx),w(m) real(RKIND), intent(inout) :: u(m),t(nest),c(nc),wrk(lwrk) integer, intent(inout) :: iwrk(nest) ! ..local scalars.. real(RKIND) :: per,dist integer :: i,ia1,ia2,ib,ifp,ig1,ig2,iq,iz,i1,i2,j1,j2,k1,k2,lwest,m1,nmin,ncc ! we set up the parameters tol and maxit integer, parameter :: maxit = 20 real(RKIND), parameter :: tol = smallnum03 ! before starting computations a data check is made. if the input data ! are invalid, control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR k1 = k+1 k2 = k1+1 m1 = m-1 nmin = 2*k1 ncc = nest*idim lwest = m*k1+nest*(7+idim+5*k) if (iopt<(-1) .or. iopt>1) return if (ipar<0 .or. ipar>1) return if (idim<=0 .or. idim>MAX_IDIM) return if (k<=0 .or. k>5) return if (m<2 .or. nest<nmin) return if (mx<m*idim .or. nc<ncc) return if (lwrk<lwest) return ! Check closed curve (1st and last points match) if (any(not_equal(x(1:idim),x((m-1)*idim+1:m*idim)))) return ! Normalized cumulative length parameter coordinate along the curve if (ipar==0 .and. iopt<=0) then i1 = 0 i2 = idim u(1) = zero do i=2,m dist = zero do j1=1,idim i1 = i1+1 i2 = i2+1 dist = dist+(x(i2)-x(i1))**2 end do u(i) = u(i-1)+sqrt(dist) end do if (u(m)<=zero) return u(2:m) = u(2:m)/u(m) u(m) = one endif if (w(1)<=zero) return if (any(u(1:m1)>=u(2:m) .or. w(1:m1)<=zero)) return if (iopt>=0) then if (s<zero) return if (equal(s,zero) .and. nest<(m+2*k)) return else if (n<=nmin .or. n>nest) return per = u(m)-u(1) j1 = k1 t(j1) = u(1) i1 = n-k t(i1) = u(m) j2 = j1 i2 = i1 do i=1,k i1 = i1+1 i2 = i2-1 j1 = j1+1 j2 = j2-1 t(j2) = t(i2)-per t(i1) = t(j1)+per end do ier = fpchep(u,m,t,n,k) if (ier/=FITPACK_OK) return end if ier = FITPACK_OK ! we partition the working space and determine the spline approximation. ifp = 1 iz = ifp+nest ia1 = iz+ncc ia2 = ia1+nest*k1 ib = ia2+nest*k ig1 = ib+nest*k2 ig2 = ig1+nest*k2 iq = ig2+nest*k1 call fpclos(iopt,idim,m,u,mx,x,w,k,s,nest,tol,maxit,k1,k2,n,t,ncc,c,fp, & wrk(ifp),wrk(iz),wrk(ia1),wrk(ia2),wrk(ib),wrk(ig1),wrk(ig2),wrk(iq),iwrk,ier) return end subroutine clocur ! given the set of data points (x(i),y(i)) and the set of positive numbers w(i),i=1,2,...,m, ! subroutine cocosp determines the weighted least-squares cubic spline s(x) with given knots ! t(j),j=1,2,...,n which satisfies the following concavity/convexity conditions ! s''(t(j+3))*e(j) <= 0, j=1,2,...n-6 ! the fit is given in the b-spline representation( b-spline coefficients c(j),j=1,2,...n-4) and ! can be evaluated by means of subroutine splev. ! pure subroutine cocosp(m,x,y,w,n,t,e,maxtr,maxbin,c,sq,sx,bind,wrk,lwrk,iwrk,kwrk,ier) ! calling sequence: ! call cocosp(m,x,y,w,n,t,e,maxtr,maxbin,c,sq,sx,bind,wrk,lwrk,iwrk,kwrk,ier) ! ! parameters: ! m : integer. on entry m must specify the number of data points. m > 3. unchanged on exit. ! x : real array of dimension at least (m). before entry, x(i) must be set to the i-th value ! of the independent variable x, for i=1,2,...,m. ! these values must be supplied in strictly ascending order. unchanged on exit. ! y : real array of dimension at least (m). before entry, y(i) must be set to the i-th value ! of the dependent variable y, for i=1,2,...,m. unchanged on exit. ! w : real array of dimension at least (m). before entry, w(i) must be set to the i-th value ! in the set of weights. the w(i) must be strictly positive. unchanged on exit. ! n : integer. on entry n must contain the total number of knots of the cubic spline. m+4>=n>=8. ! unchanged on exit. ! t : real array of dimension at least (n). before entry, this array must contain the knots of ! the spline, i.e. the position of the interior knots t(5),t(6),...,t(n-4) as well as the ! position of the boundary knots t(1),t(2),t(3),t(4) and t(n-3),t(n-2),t(n-1),t(n) needed ! for the b-spline representation. unchanged on exit. see also the restrictions (ier=10). ! e : real array of dimension at least (n). before entry, e(j) must be set to 1 if s(x) must be ! locally concave at t(j+3), to (-1) if s(x) must be locally convex at t(j+3) and to 0 if ! no convexity constraint is imposed at t(j+3),j=1,2,..,n-6. e(n-5),...,e(n) are not used. ! unchanged on exit. ! maxtr : integer. on entry maxtr must contain an over-estimate of the total number of records in ! the used tree structure, to indicate the storage space available to the routine. maxtr>=1 ! in most practical situation maxtr=100 will be sufficient. always large enough is ! n-5 n-6 ! maxtr = ( ) + ( ) with l the greatest ! l l+1 ! integer <= (n-6)/2 . unchanged on exit. ! maxbin: integer. on entry maxbin must contain an over-estimate of the number of knots where s(x) ! will have a zero second derivative maxbin >=1. in most practical situation maxbin = 10 ! will be sufficient. always large enough is maxbin=n-6. unchanged on exit. ! c : real array of dimension at least (n). on successful exit, this array will contain the ! coefficients c(1),c(2),..,c(n-4) in the b-spline representation of s(x) ! sq : real. on successful exit, sq contains the weighted sum of squared residuals of the spline ! approximation returned. ! sx : real array of dimension at least m. on successful exit this array will contain the spline ! values s(x(i)),i=1,...,m ! bind : logical array of dimension at least (n). on successful exit this array will indicate the ! knots where s''(x)=0, i.e. ! s''(t(j+3)) == 0 if bind(j) = .true. ! s''(t(j+3)) /= 0 if bind(j) = .false., j=1,2,...,n-6 ! wrk : real array of dimension at least m*4+n*7+maxbin*(maxbin+n+1). used as working space. ! lwrk : integer. on entry,lwrk must specify the actual dimension of the array wrk as declared in ! the calling (sub)program. lwrk must not be too small (see wrk). unchanged on exit. ! iwrk : integer array of dimension at least (maxtr*4+2*(maxbin+1)). used as working space. ! kwrk : integer. on entry,kwrk must specify the actual dimension of the array iwrk as declared in ! the calling (sub)program. kwrk must not be too small (see iwrk). unchanged on exit. ! ier : integer. error flag ! ier=0 : successful exit. ! ier>0 : abnormal termination: no approximation is returned ! ier=1 : the number of knots where s''(x)=0 exceeds maxbin. likely cause: maxbin too small. ! ier=2 : the number of records in the tree structure exceeds maxtr. ! probably causes : maxtr too small. ! ier=3 : the algorithm finds no solution to the posed quadratic programming problem. ! probably causes : rounding errors. ! ier=10 : on entry, the input data are controlled on validity. the following restrictions ! must be satisfied: ! m>3, maxtr>=1, maxbin>=1, 8<=n<=m+4,w(i) > 0, ! x(1)<x(2)<...<x(m), t(1)<=t(2)<=t(3)<=t(4)<=x(1), ! x(1)<t(5)<t(6)<...<t(n-4)<x(m)<=t(n-3)<=...<=t(n), ! kwrk>=maxtr*4+2*(maxbin+1), ! lwrk>=m*4+n*7+maxbin*(maxbin+n+1), ! the schoenberg-whitney conditions, i.e. there must be a subset of data points ! xx(j) such that ! t(j) < xx(j) < t(j+4), j=1,2,...,n-4 ! if one of these restrictions is found to be violated, control is immediately ! handled back to the calling program ! ! ! other subroutines required: ! fpcosp,fpbspl,fpadno,fpdeno,fpseno,fpfrno,fpchec ! ! references: ! dierckx p. : an algorithm for cubic spline fitting with convexity constraints, ! computing 24 (1980) 349-371. ! dierckx p. : an algorithm for least-squares cubic spline fitting with convexity and concavity ! constraints, report tw39, dept. computer science, k.u.leuven, 1978. ! dierckx p. : curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author: ! p. dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! creation date : march 1978 ! ! .. ! ..scalar arguments.. real(RKIND), intent(out) :: sq integer, intent(in) :: m,n,maxtr,maxbin,lwrk,kwrk integer, intent(out) :: ier ! ..array arguments.. real(RKIND), intent(in) :: x(m),y(m),w(m),t(n) real(RKIND), intent(inout) :: e(n) real(RKIND), intent(out) :: c(n),sx(m) real(RKIND), intent(inout) :: wrk(lwrk) integer, intent(inout) :: iwrk(kwrk) logical, intent(out) :: bind(n) ! ..local scalars.. integer :: ia,ib,ic,iq,iu,iz,izz,ji,jib,jjb,jl,jr,ju,kwest,lwest,mb,nm,n6 ! before starting computations a data check is made. if the input data ! are invalid, control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR lwest = 7*n+m*4+maxbin*(1+n+maxbin) kwest = 4*maxtr+2*(maxbin+1) n6 = n-6 if (m<4 .or. n<8) return if (maxtr<1 .or. maxbin<1) return if (lwrk<lwest .or. kwrk<kwest) return if (any(w<=zero)) return if (any(x(1:m-1)>=x(2:m))) return ier = fpchec(x,m,t,n,3); if (ier/=FITPACK_OK) return ! set numbers e(i) where (not_equal(e,zero)) e = sign(one,e) ! we partition the working space and determine the spline approximation nm = n+maxbin mb = maxbin+1 ia = 1 ib = ia+4*n ic = ib+nm*maxbin iz = ic+n izz = iz+n iu = izz+n iq = iu+maxbin ji = 1 ju = ji+maxtr jl = ju+maxtr jr = jl+maxtr jjb = jr+maxtr jib = jjb+mb call fpcosp(m,x,y,w,n,t,e,maxtr,maxbin,c,sq,sx,bind,nm,mb,wrk(ia), & wrk(ib),wrk(ic),wrk(iz),wrk(izz),wrk(iu),wrk(iq),iwrk(ji), & iwrk(ju),iwrk(jl),iwrk(jr),iwrk(jjb),iwrk(jib),ier) return end subroutine cocosp ! given the set of data points (x(i),y(i)) and the set of positive numbers w(i), i=1,2,...,m, ! subroutine concon determines a cubic spline approximation s(x) which satisfies the following ! local convexity constraints s''(x(i))*v(i) <= 0, i=1,2,...,m. the number of knots n and the ! position t(j),j=1,2,...n is chosen automatically by the routine in a way that ! sq = sum((w(i)*(y(i)-s(x(i))))**2) be <= s. ! the fit is given in the b-spline representation (b-spline coefficients c(j),j=1,2,...n-4) and ! can be evaluated by means of subroutine splev. pure subroutine concon(iopt,m,x,y,w,v,s,nest,maxtr,maxbin, & n,t,c,sq,sx,bind,wrk,lwrk,iwrk,kwrk,ier) ! ! calling sequence: ! ! call concon(iopt,m,x,y,w,v,s,nest,maxtr,maxbin,n,t,c,sq,sx,bind,wrk,lwrk,iwrk,kwrk,ier) ! ! parameters: ! iopt: integer flag. if iopt=0, the routine will start with the minimal number of knots to ! guarantee that the convexity conditions will be satisfied. if iopt=1, the routine will ! continue with the set of knots found at the last call of the routine. attention: a call ! with iopt=1 must always be immediately preceded by another call with iopt=1 or iopt=zero ! unchanged on exit. ! m : integer. on entry m must specify the number of data points. m > 3. unchanged on exit. ! x : real array of dimension at least (m). before entry, x(i) must be set to the i-th value of ! the independent variable x, for i=1,2,...,m. these values must be supplied in strictly ! ascending order. unchanged on exit. ! y : real array of dimension at least (m). before entry, y(i) must be set to the i-th value of ! the dependent variable y, for i=1,2,...,m. unchanged on exit. ! w : real array of dimension at least (m). before entry, w(i) must be set to the i-th value in ! the set of weights. the w(i) must be strictly positive. unchanged on exit. ! v : real array of dimension at least (m). before entry, v(i) must be set to 1 if s(x) must be ! locally concave at x(i), to (-1) if s(x) must be locally convex at x(i) and to 0 if no ! convexity constraint is imposed at x(i). ! s : real. on entry s must specify an over-estimate for the the weighted sum of squared ! residuals sq of the requested spline. s >=zero unchanged on exit. ! nest : integer. on entry nest must contain an over-estimate of the total number of knots of the ! spline returned, to indicate the storage space available to the routine. nest >=8. in ! most practical situation nest=m/2 will be sufficient. always large enough is nest=m+4. ! unchanged on exit. ! maxtr : integer. on entry maxtr must contain an over-estimate of the total number of records in ! the used tree structure, to indicate the storage space available to the routine. maxtr ! >=1 in most practical situation maxtr=100 will be sufficient. always large enough is ! nest-5 nest-6 ! maxtr = ( ) + ( ) with l the greatest ! l l+1 ! integer <= (nest-6)/2 . unchanged on exit. ! maxbin: integer. on entry maxbin must contain an over-estimate of the number of knots where s(x) ! will have a zero second derivative maxbin >=1. in most practical situation maxbin = 10 ! will be sufficient. always large enough is maxbin=nest-6. unchanged on exit. ! n : integer. on exit with ier <=0, n will contain the total number of knots of the spline ! approximation returned. if the computation mode iopt=1 is used this value of n should be ! left unchanged between subsequent calls. ! t : real array of dimension at least (nest). on exit with ier<=0, this array will contain the ! knots of the spline,i.e. the position of the interior knots t(5),t(6),...,t(n-4) as well ! as the position of the additional knots t(1)=t(2)=t(3)=t(4)=x(1) and t(n-3)=t(n-2)=t(n-1) ! =t(n)=x(m) needed for the the b-spline representation. if the computation mode iopt=1 is ! used, the values of t(1),t(2),...,t(n) should be left unchanged between subsequent calls. ! c : real array of dimension at least (nest). on successful exit, this array will contain the ! coefficients c(1),c(2),..,c(n-4) in the b-spline representation of s(x) ! sq : real. unless ier>0 , sq contains the weighted sum of squared residuals of the spline ! approximation returned. ! sx : real array of dimension at least m. on exit with ier<=0 this array will contain the ! spline values s(x(i)),i=1,...,m if the computation mode iopt=1 is used, the values of ! sx(1),sx(2),...,sx(m) should be left unchanged between subsequent calls. ! bind: logical array of dimension at least nest. on exit with ier<=0 this array will indicate ! the knots where s''(x)=0, i.e. ! s''(t(j+3)) == 0 if bind(j) = .true. ! s''(t(j+3)) /= 0 if bind(j) = .false., j=1,2,...,n-6 ! if the computation mode iopt=1 is used, the values of bind(1),...,bind(n-6) should be ! left unchanged between subsequent calls. ! wrk : real array of dimension at least (m*4+nest*8+maxbin*(maxbin+nest+1)). used as working ! space. ! lwrk : integer. on entry,lwrk must specify the actual dimension of the array wrk as declared in ! the calling (sub)program. lwrk must not be too small (see wrk). unchanged on exit. ! iwrk : integer array of dimension at least (maxtr*4+2*(maxbin+1)) used as working space. ! kwrk : integer. on entry,kwrk must specify the actual dimension of the array iwrk as declared in ! the calling (sub)program. kwrk must not be too small (see iwrk). unchanged on exit. ! ier : integer. error flag ! ier=0 : normal return, s(x) satisfies the concavity/convexity constraints and sq <= s. ! ier<0 : abnormal termination: s(x) satisfies the concavity/convexity constraints but sq > s. ! ier=-3 : the requested storage space exceeds the available storage space as specified by ! the parameter nest. probably causes: nest too small. if nest is already large (say ! nest > m/2), it may also indicate that s is too small. the approximation returned ! is the least-squares cubic spline according to the knots t(1),...,t(n) (n=nest) ! which satisfies the convexity constraints. ! ier=-2 : the maximal number of knots n=m+4 has been reached. probably causes: s too small. ! ier=-1 : the number of knots n is less than the maximal number m+4 but concon finds that ! adding one or more knots will not further reduce the value of sq. probably causes: ! s too small. ! ier>0 : abnormal termination: no approximation is returned ! ier=1 : the number of knots where s''(x)=0 exceeds maxbin. probably causes : maxbin too ! small. ! ier=2 : the number of records in the tree structure exceeds maxtr. ! probably causes : maxtr too small. ! ier=3 : the algorithm finds no solution to the posed quadratic programming problem. ! probably causes : rounding errors. ! ier=4 : the minimum number of knots (given by n) to guarantee that the concavity/convexity ! conditions will be satisfied is greater than nest. probably causes: nest too small. ! ier=5 : the minimum number of knots (given by n) to guarantee that the concavity/convexity ! conditions will be satisfied is greater than m+4. probably causes: strongly ! alternating convexity and concavity conditions. normally the situation can be ! coped with by adding n-m-4 extra data points (found by linear interpolation e.g.) ! with a small weight w(i) and a v(i) number equal to zero. ! ier=10 : on entry, the input data are controlled on validity. the following restrictions ! must be satisfied ! 0<=iopt<=1, m>3, nest>=8, s>=0, maxtr>=1, maxbin>=1, ! kwrk>=maxtr*4+2*(maxbin+1), w(i)>0, x(i) < x(i+1), ! lwrk>=m*4+nest*8+maxbin*(maxbin+nest+1) ! if one of these restrictions is found to be violated ! control is immediately repassed to the calling program ! ! further comments: ! as an example of the use of the computation mode iopt=1, the following program segment will ! cause concon to return control each time a spline with a new set of knots has been computed. ! ............. ! iopt = 0 ! s = 0.1e+60 (s very large) ! do i=1,m ! call concon(iopt,m,x,y,w,v,s,nest,maxtr,maxbin,n,t,c,sq,sx,bind,wrk,lwrk,iwrk,kwrk,ier) ! ...... ! s = sq ! iopt=1 ! end do ! ............. ! ! other subroutines required: ! fpcoco,fpcosp,fpbspl,fpadno,fpdeno,fpseno,fpfrno ! ! references: ! dierckx p. : an algorithm for cubic spline fitting with convexity constraints, ! computing 24 (1980) 349-371. ! dierckx p. : an algorithm for least-squares cubic spline fitting with convexity and concavity ! constraints, report tw39, dept. computer science, k.u.leuven, 1978. ! dierckx p. : curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author: ! p. dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! creation date : march 1978 ! ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: s real(RKIND), intent(out) :: sq integer, intent(in) :: iopt,m,nest,maxtr,maxbin,lwrk,kwrk integer, intent(inout) :: n integer, intent(out) :: ier ! ..array arguments.. real(RKIND), intent(in) :: x(m),y(m),w(m) real(RKIND), intent(inout) :: v(m),t(nest),c(nest),sx(m),wrk(lwrk) integer, intent(inout) :: iwrk(kwrk) logical, intent(inout) :: bind(nest) ! ..local scalars.. integer :: lwest,kwest,ie,iw,lww ! before starting computations a data check is made. if the input data are invalid, control is ! immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR lwest = 8*nest+m*4+maxbin*(1+nest+maxbin) kwest = 4*maxtr+2*(maxbin+1) if (iopt<0 .or. iopt>1) return if (m<4 .or. nest<8) return if (s<zero) return if (maxtr<1 .or. maxbin<1) return if (lwrk<lwest .or. kwrk<kwest) return if (iopt==0) then ! Zero weights if (any(w<=zero)) return ! Non-monotonic x if (any(x(2:m)<=x(1:m-1))) return where (not_equal(v,zero)) v = sign(one,v) endif ier = FITPACK_OK ! we partition the working space and determine the spline approximation ie = 1 iw = ie+nest lww = lwrk-nest call fpcoco(iopt,m,x,y,w,v,s,nest,maxtr,maxbin,n,t,c,sq,sx, & bind,wrk(ie),wrk(iw),lww,iwrk,kwrk,ier) return end subroutine concon ! given the ordered set of m points x(i) in the idim-dimensional space and given also a ! corresponding set of strictly increasing values u(i) and the set of positive numbers ! w(i),i=1,2,...,m, subroutine concur determines a smooth approximating spline curve s(u), i.e. ! x1 = s1(u) ! x2 = s2(u) ub = u(1) <= u <= u(m) = ue ! ......... ! xidim = sidim(u) ! with sj(u),j=1,2,...,idim spline functions of odd degree k with common knots ! t(j),j=1,2,...,n. ! in addition these splines will satisfy the following boundary constraints ! (l) ! if ib > 0 : sj (u(1)) = db(idim*l+j) ,l=0,1,...,ib-1 ! and (l) ! if ie > 0 : sj (u(m)) = de(idim*l+j) ,l=0,1,...,ie-1. ! if iopt=-1 concur calculates the weighted least-squares spline curve according to a given set ! of knots. ! if iopt>=0 the number of knots of the splines sj(u) and the position t(j),j=1,2,...,n is ! chosen automatically by the routine. the smoothness of s(u) is then achieved by minimalizing ! the discontinuity jumps of the k-th derivative of s(u) at the knots t(j),j=k+2,k+3,..., ! n-k-1. the amount of smoothness is determined by the condition that ! f(p)=sum((w(i)*dist(x(i),s(u(i))))**2) be <= s, with s a given non-negative constant, ! called the smoothing factor. the fit s(u) is given in the b-spline representation and can ! be evaluated by means of subroutine curev. subroutine concur(iopt,idim,m,u,mx,x,xx,w,ib,db,nb, & ie,de,ne,k,s,nest,n,t,nc,c,np,cp,fp,wrk,lwrk,iwrk,ier) ! ! calling sequence: ! call concur(iopt,idim,m,u,mx,x,xx,w,ib,db,nb,ie,de,ne,k,s,nest,n, & ! t,nc,c,np,cp,fp,wrk,lwrk,iwrk,ier) ! ! parameters: ! iopt : integer flag. on entry iopt must specify whether a weighted least-squares spline curve ! (iopt=-1) or a smoothing spline curve (iopt=0 or 1) must be determined.if iopt=0 the ! routine will start with an initial set of knots t(i)=ub,t(i+k+1)=ue, i=1,2,...,k+1. ! if iopt=1 the routine will continue with the knots found at the last call of the routine. ! attention: a call with iopt=1 must always be immediately preceded by another call with ! iopt=1 or iopt=zero. unchanged on exit. ! idim : integer. on entry idim must specify the dimension of the curve. 0 < idim < 11. ! unchanged on exit. ! m : integer. on entry m must specify the number of data points. m>k-max(ib-1,0)-max(ie-1,0). ! unchanged on exit. ! u : real array of dimension at least (m). before entry, u(i) must be set to the i-th value ! of the parameter variable u for i=1,2,...,m. these values must be supplied in strictly ! ascending order and will be unchanged on exit. ! mx : integer. on entry mx must specify the actual dimension of the arrays x and xx as decla- ! red in the calling (sub)program mx must not be too small (see x). unchanged on exit. ! x : real array of dimension at least idim*m. ! before entry, x(idim*(i-1)+j) must contain the j-th coordinate of the i-th data point ! for i=1,2,...,m and j=1,2,...,idim. unchanged on exit. ! xx : real array of dimension at least idim*m. used as working space. on exit xx contains the ! coordinates of the data points to which a spline curve with zero derivative constraints ! has been determined. if the computation mode iopt =1 is used xx should be left unchanged ! between calls. ! w : real array of dimension at least (m). before entry, w(i) must be set to the i-th value ! in the set of weights. the w(i) must be strictly positive. unchanged on exit. ! see also further comments. ! ib : integer. on entry ib must specify the number of derivative constraints for the curve at ! the begin point. 0<=ib<=(k+1)/2 unchanged on exit. ! db : real array of dimension nb. before entry db(idim*l+j) must contain the l-th order deri- ! vative of sj(u) at u=u(1) for j=1,2,...,idim and l=0,1,...,ib-1 (if ib>0). ! unchanged on exit. ! nb : integer, specifying the dimension of db. nb>=max(1,idim*ib). unchanged on exit. ! ie : integer. on entry ie must specify the number of derivative constraints for the curve at ! the end point. 0<=ie<=(k+1)/2. unchanged on exit. ! de : real array of dimension ne. before entry de(idim*l+j) must contain the l-th order deri- ! vative of sj(u) at u=u(m) for j=1,2,...,idim and l=0,1,...,ie-1 (if ie>0). ! unchanged on exit. ! ne : integer, specifying the dimension of de. ne>=max(1,idim*ie) unchanged on exit. ! k : integer. on entry k must specify the degree of the splines. k=1,3 or 5. unchanged on exit. ! s : real.on entry (in case iopt>=0) s must specify the smoothing factor. s >=zero unchanged ! on exit. for advice on the choice of s see further comments. ! nest : integer. on entry nest must contain an over-estimate of the total number of knots of the ! splines returned, to indicate the storage space available to the routine. nest >=2*k+2. ! in most practical situation nest=m/2 will be sufficient. always large enough is ! nest=m+k+1+max(0,ib-1)+max(0,ie-1), the number of knots needed for interpolation (s=0). ! unchanged on exit. ! n : integer. unless ier = 10 (in case iopt >=0), n will contain the total number of knots of ! the smoothing spline curve returned. if the computation mode iopt=1 is used this value ! of n should be left unchanged between subsequent calls. in case iopt=-1, the value of n ! must be specified on entry. ! t : real array of dimension at least (nest). on successful exit, this array will contain the ! knots of the spline curve,i.e. the position of the interior knots t(k+2),t(k+3),.., ! t(n-k-1) as well as the position of the additional t(1)=t(2)=...=t(k+1)=ub and t(n-k)= ! ...=t(n)=ue needed for the b-spline representation. ! if the computation mode iopt=1 is used, the values of t(1),t(2),...,t(n) should be left ! unchanged between subsequent calls. if the computation mode iopt=-1 is used, the values ! t(k+2),...,t(n-k-1) must be supplied by the user, before entry. see also the restric- ! tions (ier=10). ! nc : integer. on entry nc must specify the actual dimension of the array c as declared in the ! calling (sub)program. nc must not be too small (see c). unchanged on exit. ! c : real array of dimension at least (nest*idim). on successful exit, this array will con- ! tain the coefficients in the b-spline representation of the spline curve s(u),i.e. ! the b-spline coefficients of the spline sj(u) will be given in c(n*(j-1)+i),i=1,2,..., ! n-k-1 for j=1,2,...,idim. ! cp : real array of dimension at least 2*(k+1)*idim. on exit cp will contain the b-spline ! coefficients of a polynomial curve which satisfies the boundary constraints. if the ! computation mode iopt =1 is used cp should be left unchanged between calls. ! np : integer. on entry np must specify the actual dimension of the array cp as declared in ! the calling (sub)program. np must not be too small (see cp). unchanged on exit. ! fp : real. unless ier = 10, fp contains the weighted sum of squared residuals of the spline ! curve returned. ! wrk : real array of dimension at least m*(k+1)+nest*(6+idim+3*k). used as working space. if ! the computation mode iopt=1 is used, the values wrk(1),...,wrk(n) should be left ! unchanged between subsequent calls. ! lwrk : integer. on entry,lwrk must specify the actual dimension of the array wrk as declared ! in the calling (sub)program. lwrk must not be too small (see wrk). unchanged on exit. ! iwrk : integer array of dimension at least (nest). used as working space. if the computation ! mode iopt=1 is used,the values iwrk(1),...,iwrk(n) should be left unchanged between ! subsequent calls. ! ier : integer. unless the routine detects an error, ier contains a non-positive value on exit ! i.e. ! ier=0 : normal return. the curve returned has a residual sum of squares fp such that ! abs(fp-s)/s <= tol with tol a relative tolerance set to 0.001 by the program. ! ier=-1 : normal return. the curve returned is an interpolating spline curve, satisfying the ! constraints (fp=0). ! ier=-2 : normal return. the curve returned is the weighted least-squares polynomial curve of ! degree k, satisfying the constraints. in this extreme case fp gives the upper ! bound fp0 for the smoothing factor s. ! ier=1 : error. the required storage space exceeds the available storage space, as specified ! by the parameter nest. probably causes : nest too small. if nest is already large ! (say nest > m/2), it may also indicate that s is too small ! the approximation returned is the least-squares spline curve according to the knots ! t(1),t(2),...,t(n). (n=nest) the parameter fp gives the corresponding weighted sum of ! squared residuals (fp>s). ! ier=2 : error. a theoretically impossible result was found during the iteration process for ! finding a smoothing spline curve with fp = s. probably causes : s too small. there is ! an approximation returned but the corresponding weighted sum of squared residuals ! does not satisfy the condition abs(fp-s)/s < tol. ! ier=3 : error. the maximal number of iterations maxit (set to 20 by the program) allowed for ! finding a smoothing curve with fp=s has been reached. probably causes : s too small ! there is an approximation returned but the corresponding weighted sum of squared re- ! siduals does not satisfy the condition abs(fp-s)/s < tol. ! ier=10 : error. on entry, the input data are controlled on validity the following restrictions ! must be satisfied. ! -1<=iopt<=1, k = 1,3 or 5, m>k-max(0,ib-1)-max(0,ie-1), ! nest>=2k+2, 0<idim<=10, lwrk>=(k+1)*m+nest*(6+idim+3*k), ! nc >=nest*idim ,u(1)<u(2)<...<u(m),w(i)>0 i=1,2,...,m, ! mx>=idim*m,0<=ib<=(k+1)/2,0<=ie<=(k+1)/2,nb>=1,ne>=1, ! nb>=ib*idim,ne>=ib*idim,np>=2*(k+1)*idim, ! if iopt=-1: 2*k+2<=n<=min(nest,mmax) with mmax = m+k+1+max(0,ib-1)+max(0,ie-1) ! u(1)<t(k+2)<t(k+3)<...<t(n-k-1)<u(m) ! the schoenberg-whitney conditions, i.e. there must be a subset of data ! points uu(j) such that ! t(j) < uu(j) < t(j+k+1), j=1+max(0,ib-1),...,n+k-1-max(0,ie-1) ! if iopt>=0: s>=0 ! if s=0 : nest >=mmax (see above) ! if one of these conditions is found to be violated, control is immediately repassed to ! the calling program. in that case there is no approximation returned. ! ! further comments: ! by means of the parameter s, the user can control the tradeoff between closeness of fit and ! smoothness of fit of the approximation. if s is too large, the curve will be too smooth and ! ! signal will be lost ; if s is too small the curve will pick up too much noise. in the extreme ! cases the program will return an interpolating curve if s=0 and the least-squares polynomial ! curve of degree k if s is very large. between these extremes, a properly chosen s will result ! in a good compromise between closeness of fit and smoothness of fit. to decide whether an appro- ! ximation, corresponding to a certain s is satisfactory the user is highly recommended to inspect ! the fits graphically. ! recommended values for s depend on the weights w(i). if these are taken as 1/d(i) with d(i) an ! estimate of the standard deviation of x(i), a good s-value should be found in the range ! (m-sqrt(2*m),m+sqrt(2*m)). if nothing is known about the statistical error in x(i) each w(i) can ! be set equal to one and s determined by trial and error, taking account of the comments above. ! the best is then to start with a very large value of s ( to determine the least-squares ! polynomial curve and the upper bound fp0 for s) and then to progressively decrease the value of ! s ( say by a factor 10 in the beginning, i.e. s=fp0/10, fp0/100,...and more carefully as the ! approximating curve shows more detail) to obtain closer fits. to economize the search for a good ! s-value the program provides with different modes of computation. at the first call of the ! routine, or whenever he wants to restart with the initial set of knots the user must set iopt=0 ! if iopt=1 the program will continue with the set of knots found at the last call of the routine. ! this will save a lot of computation time if concur is called repeatedly for different values of ! s. the number of knots of the spline returned and their location will depend on the value of s ! and on the complexity of the shape of the curve underlying the data. but, if the computation ! mode iopt=1 is used, the knots returned may also depend on the s-values at previous calls (if ! these were smaller). therefore, if after a number of trials with different s-values and iopt=1, ! the user can finally accept a fit as satisfactory, it may be worthwhile for him to call concur ! once more with the selected value for s but now with iopt=zero indeed, concur may then return ! an approximation of the same quality of fit but with fewer knots and therefore better if data ! reduction is also an important objective for the user. ! ! the form of the approximating curve can strongly be affected by the choice of the parameter ! values u(i). if there is no physical reason for choosing a particular parameter u, often good ! results will be obtained with the choice ! v(1)=0, v(i)=v(i-1)+q(i), i=2,...,m, u(i)=v(i)/v(m), i=1,..,m ! where ! q(i)= sqrt(sum j=1,idim (xj(i)-xj(i-1))**2 ) ! other possibilities for q(i) are ! q(i)= sum j=1,idim (xj(i)-xj(i-1))**2 ! q(i)= sum j=1,idim abs(xj(i)-xj(i-1)) ! q(i)= max j=1,idim abs(xj(i)-xj(i-1)) ! q(i)= 1 ! ! other subroutines required: ! fpback,fpbspl,fpched,fpcons,fpdisc,fpgivs,fpknot,fprati,fprota ! curev,fppocu,fpadpo,fpinst ! ! references: ! dierckx p. : algorithms for smoothing data with periodic and parametric splines, ! computer graphics and image processing 20 (1982) 171-184. ! dierckx p. : algorithms for smoothing data with periodic and parametric splines, report tw55, ! dept. computer science, k.u.leuven, 1981. ! dierckx p. : curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author: ! p.dierckx ! dept. computer science, k.u. leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! creation date : may 1979 ! ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: s real(RKIND), intent(inout) :: fp integer, intent(in) :: iopt,idim,m,mx,ib,nb,ie,ne,k,nest,nc,np,lwrk integer, intent(inout) :: n integer, intent(out) :: ier ! ..array arguments.. real(RKIND), intent(in) :: u(m),x(mx),db(nb),de(ne),w(m) real(RKIND), intent(inout) :: xx(mx),t(nest),c(nc),cp(np),wrk(lwrk) integer , intent(inout) :: iwrk(nest) ! ..local scalars.. integer :: ib1,ie1,ja,jb,jfp,jg,jq,jz,k1,k2,lwest,nmin,ncc,kk,mmin,nmax,mxx ! .. ! we set up the parameters tol and maxit real(RKIND), parameter :: tol = smallnum03 integer, parameter :: maxit = 20 ! before starting computations a data check is made. if the input data ! are invalid, control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR k1 = k+1 k2 = k+2 kk = k1/2 nmin = 2*k1 ib1 = max(0,ib-1) ie1 = max(0,ie-1) mmin = k1-ib1-ie1 mxx = m*idim ncc = nest*idim lwest = m*k1+nest*(6+idim+3*k) if (iopt<(-1) .or. iopt>1) return if (idim<=0 .or. idim>MAX_IDIM) return if (k<=0 .or. k>5) return if (kk*2/=k1) return if (ib<0 .or. ib>kk) return if (ie<0 .or. ie>kk) return if (m<mmin .or. nest<nmin) return if (nb<(idim*ib) .or. ne<(idim*ie)) return if (np<(2*k1*idim)) return if (mx<mxx .or. nc<ncc) return if (lwrk<lwest) return if (any(w<=zero)) return if (any(u(1:m-1)>=u(2:m))) return if (iopt<0) then if (n<nmin .or. n>nest) return t(1:k1) = u(1) t(n-k:n) = u(m) ier = fpched(u,m,t,n,k,ib,ie) if (ier/=FITPACK_OK) return else nmax = m+k1+ib1+ie1 if (s<zero) return if (equal(s,zero) .and. nest<nmax) return ier = FITPACK_OK endif if (iopt<=0) then ! we determine a polynomial curve satisfying the boundary constraints. call fppocu(idim,k,u(1),u(m),ib,db,nb,ie,de,ne,cp,np) ! we generate new data points which will be approximated by a spline ! with zero derivative constraints. wrk(1:k1) = u(1) wrk(nmin-k1+1:nmin) = u(m) ! evaluate the polynomial curve call curev(idim,wrk,nmin,cp,np,k,u,m,xx,mxx,ier) ! subtract from the old data, the values of the polynomial curve xx(1:mxx) = x(1:mxx)-xx(1:mxx) endif ! we partition the working space and determine the spline curve. jfp = 1 jz = jfp+nest ja = jz+ncc jb = ja+nest*k1 jg = jb+nest*k2 jq = jg+nest*k2 call fpcons(iopt,idim,m,u,mxx,xx,w,ib,ie,k,s,nest,tol,maxit,k1, & k2,n,t,ncc,c,fp,wrk(jfp),wrk(jz),wrk(ja),wrk(jb),wrk(jg),wrk(jq),iwrk,ier) ! add the polynomial curve to the calculated spline. call fpadpo(idim,t,n,c,ncc,k,cp,np,wrk(jz),wrk(ja),wrk(jb)) return end subroutine concur ! subroutine cualde evaluates at the point u all the derivatives ! (l) ! d(idim*l+j) = sj (u) ,l=0,1,...,k, j=1,2,...,idim ! of a spline curve s(u) of order k1 (degree k=k1-1) and dimension idim ! given in its b-spline representation. pure subroutine cualde(idim,t,n,c,nc,k1,u,d,nd,ier) ! ! input parameters: ! idim : integer, giving the dimension of the spline curve. ! t : array,length n, which contains the position of the knots. ! n : integer, giving the total number of knots of s(u). ! c : array,length nc, which contains the b-spline coefficients. ! nc : integer, giving the total number of coefficients of s(u). ! k1 : integer, giving the order of s(u) (order=degree+1). ! u : real, which contains the point where the derivatives must be evaluated. ! nd : integer, giving the dimension of the array d. nd >= k1*idim ! ! output parameters: ! d : array,length nd,giving the different curve derivatives. d(idim*l+j) will contain the ! j-th coordinate of the l-th derivative of the curve at the point u. ! ier : error flag ! ier = 0 : normal return ! ier =10 : invalid input data (see restrictions) ! ! restrictions: ! nd >= k1*idim ! t(k1) <= u <= t(n-k1+1) ! ! further comments: ! if u coincides with a knot, right derivatives are computed ! ( left derivatives if u = t(n-k1+1) ). ! ! other subroutines required: fpader. ! ! references : ! de boor c : on calculating with b-splines, j. approximation theory ! 6 (1972) 50-62. ! cox m.g. : the numerical evaluation of b-splines, j. inst. maths ! applics 10 (1972) 134-149. ! dierckx p. : curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! latest update : march 1987 ! ! ..scalar arguments.. integer, intent(in) :: idim,n,nc,k1,nd integer, intent(out) :: ier real(RKIND), intent(in) :: u ! ..array arguments.. real(RKIND), intent(in) :: t(n),c(nc) real(RKIND), intent(out) :: d(nd) ! ..local scalars.. integer :: i,j,kk,l,m,nk1 ! ..local array.. real(RKIND) :: h(MAX_ORDER+1) ! .. ! before starting computations a data check is made. if the input data ! are invalid control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR if(nd<(k1*idim)) return nk1 = n-k1 if(u<t(k1) .or. u>t(nk1+1)) return ! search for knot interval t(l) <= u < t(l+1) l = k1 do while (.not.(u<t(l+1) .or. l==nk1)) l = l+1 end do if(t(l)>=t(l+1)) return ier = FITPACK_OK ! calculate the derivatives. j = 1 do i=1,idim call fpader(t,n,c(j),k1,u,l,h) m = i do kk=1,k1 d(m) = h(kk) m = m+idim end do j = j+n end do return end subroutine cualde ! subroutine curev evaluates in a number of points u(i),i=1,2,...,m a spline curve s(u) of degree k ! and dimension idim, given in its b-spline representation. pure subroutine curev(idim,t,n,c,nc,k,u,m,x,mx,ier) ! calling sequence: ! call curev(idim,t,n,c,nc,k,u,m,x,mx,ier) ! ! input parameters: ! idim : integer, giving the dimension of the spline curve. ! t : array,length n, which contains the position of the knots. ! n : integer, giving the total number of knots of s(u). ! c : array,length nc, which contains the b-spline coefficients. ! nc : integer, giving the total number of coefficients of s(u). ! k : integer, giving the degree of s(u). ! u : array,length m, which contains the points where s(u) must be evaluated. ! m : integer, giving the number of points where s(u) must be evaluated. ! mx : integer, giving the dimension of the array x. mx >= m*idim ! ! output parameters: ! x : array,length mx,giving the value of s(u) at the different points. x(idim*(i-1)+j) will ! contain the j-th coordinate of the i-th point on the curve. ! ier : error flag ! ier = 0 : normal return ! ier =10 : invalid input data (see restrictions) ! ! restrictions: ! m >= 1 ! mx >= m*idim ! t(k+1) <= u(i) <= u(i+1) <= t(n-k) , i=1,2,...,m-1. ! ! other subroutines required: fpbspl. ! ! references : ! de boor c : on calculating with b-splines, j. approximation theory 6 (1972) 50-62. ! cox m.g. : the numerical evaluation of b-splines, j. inst. maths applics 10 (1972) 134-149. ! dierckx p. : curve and surface fitting with splines, monographs on numerical analysis, oxford ! university press, 1993. ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! ..scalar arguments.. integer, intent(in) :: idim,n,nc,k,m,mx integer, intent(out) :: ier ! ..array arguments.. real(RKIND), intent(in) :: t(n),c(nc),u(m) real(RKIND), intent(out) :: x(idim,m) ! x has size (mx), assume 2d (idim,m) ! ..local scalars.. integer :: i,j1,k1,l,ll,l1,nk1 real(RKIND) :: arg,tb,te ! ..local array.. real(RKIND) h(MAX_ORDER+1) ! .. ! before starting computations a data check is made. if the input data ! are invalid control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR if (m<1) return ! Check monotonic if (m>1 .and. any(u(2:m)<u(1:m-1))) return ! Check enough output space if (mx<(m*idim)) return ier = FITPACK_OK ! fetch tb and te, the boundaries of the approximation interval. k1 = k+1 nk1 = n-k1 tb = t(k1) te = t(nk1+1) l = k1 l1 = l+1 ! main loop for the different points. eval_points: do i=1,m ! fetch a new u-value arg. arg = min(max(u(i),tb),te) ! search for knot interval t(l) <= arg < t(l+1) do while (.not.(arg<t(l1) .or. l==nk1)) l = l1 l1 = l+1 end do ! evaluate the non-zero b-splines at arg. h = fpbspl(t,n,k,arg,l) ! find the value of s(u) at u=arg. ll = l-k1 do j1=1,idim x(j1,i) = dot_product(h(1:k1),c(ll+1:ll+k1)) ll = ll+n end do end do eval_points return end subroutine curev pure subroutine curfit(iopt,m,x,y,w,xb,xe,k,s,nest,n,t,c,fp,wrk,lwrk,iwrk,ier) ! given the set of data points (x(i),y(i)) and the set of positive ! numbers w(i),i=1,2,...,m,subroutine curfit determines a smooth spline ! approximation of degree k on the interval xb <= x <= xe. ! if iopt=-1 curfit calculates the weighted least-squares spline ! according to a given set of knots. ! if iopt>=0 the number of knots of the spline s(x) and the position ! t(j),j=1,2,...,n is chosen automatically by the routine. the smooth- ! ness of s(x) is then achieved by minimalizing the discontinuity ! jumps of the k-th derivative of s(x) at the knots t(j),j=k+2,k+3,..., ! n-k-1. the amount of smoothness is determined by the condition that ! f(p)=sum((w(i)*(y(i)-s(x(i))))**2) be <= s, with s a given non- ! negative constant, called the smoothing factor. ! the fit s(x) is given in the b-spline representation (b-spline coef- ! ficients c(j),j=1,2,...,n-k-1) and can be evaluated by means of ! subroutine splev. ! ! calling sequence: ! call curfit(iopt,m,x,y,w,xb,xe,k,s,nest,n,t,c,fp,wrk, ! * lwrk,iwrk,ier) ! ! parameters: ! iopt : integer flag. on entry iopt must specify whether a weighted ! least-squares spline (iopt=-1) or a smoothing spline (iopt= ! 0 or 1) must be determined. if iopt=0 the routine will start ! with an initial set of knots t(i)=xb, t(i+k+1)=xe, i=1,2,... ! k+1. if iopt=1 the routine will continue with the knots ! found at the last call of the routine. ! attention: a call with iopt=1 must always be immediately ! preceded by another call with iopt=1 or iopt=zero ! unchanged on exit. ! m : integer. on entry m must specify the number of data points. ! m > k. unchanged on exit. ! x : real array of dimension at least (m). before entry, x(i) ! must be set to the i-th value of the independent variable x, ! for i=1,2,...,m. these values must be supplied in strictly ! ascending order. unchanged on exit. ! y : real array of dimension at least (m). before entry, y(i) ! must be set to the i-th value of the dependent variable y, ! for i=1,2,...,m. unchanged on exit. ! w : real array of dimension at least (m). before entry, w(i) ! must be set to the i-th value in the set of weights. the ! w(i) must be strictly positive. unchanged on exit. ! see also further comments. ! xb,xe : real values. on entry xb and xe must specify the boundaries ! of the approximation interval. xb<=x(1), xe>=x(m). ! unchanged on exit. ! k : integer. on entry k must specify the degree of the spline. ! 1<=k<=5. it is recommended to use cubic splines (k=3). ! the user is strongly dissuaded from choosing k even,together ! with a small s-value. unchanged on exit. ! s : real.on entry (in case iopt>=0) s must specify the smoothing ! factor. s >=zero unchanged on exit. ! for advice on the choice of s see further comments. ! nest : integer. on entry nest must contain an over-estimate of the ! total number of knots of the spline returned, to indicate ! the storage space available to the routine. nest >=2*k+2. ! in most practical situation nest=m/2 will be sufficient. ! always large enough is nest=m+k+1, the number of knots ! needed for interpolation (s=0). unchanged on exit. ! n : integer. ! unless ier =10 (in case iopt >=0), n will contain the ! total number of knots of the spline approximation returned. ! if the computation mode iopt=1 is used this value of n ! should be left unchanged between subsequent calls. ! in case iopt=-1, the value of n must be specified on entry. ! t : real array of dimension at least (nest). ! on successful exit, this array will contain the knots of the ! spline,i.e. the position of the interior knots t(k+2),t(k+3) ! ...,t(n-k-1) as well as the position of the additional knots ! t(1)=t(2)=...=t(k+1)=xb and t(n-k)=...=t(n)=xe needed for ! the b-spline representation. ! if the computation mode iopt=1 is used, the values of t(1), ! t(2),...,t(n) should be left unchanged between subsequent ! calls. if the computation mode iopt=-1 is used, the values ! t(k+2),...,t(n-k-1) must be supplied by the user, before ! entry. see also the restrictions (ier=10). ! c : real array of dimension at least (nest). ! on successful exit, this array will contain the coefficients ! c(1),c(2),..,c(n-k-1) in the b-spline representation of s(x) ! fp : real. unless ier=10, fp contains the weighted sum of ! squared residuals of the spline approximation returned. ! wrk : real array of dimension at least (m*(k+1)+nest*(7+3*k)). ! used as working space. if the computation mode iopt=1 is ! used, the values wrk(1),...,wrk(n) should be left unchanged ! between subsequent calls. ! lwrk : integer. on entry,lwrk must specify the actual dimension of ! the array wrk as declared in the calling (sub)program.lwrk ! must not be too small (see wrk). unchanged on exit. ! iwrk : integer array of dimension at least (nest). ! used as working space. if the computation mode iopt=1 is ! used,the values iwrk(1),...,iwrk(n) should be left unchanged ! between subsequent calls. ! ier : integer. unless the routine detects an error, ier contains a ! non-positive value on exit, i.e. ! ier=0 : normal return. the spline returned has a residual sum of ! squares fp such that abs(fp-s)/s <= tol with tol a relat- ! ive tolerance set to 0.001 by the program. ! ier=-1 : normal return. the spline returned is an interpolating ! spline (fp=0). ! ier=-2 : normal return. the spline returned is the weighted least- ! squares polynomial of degree k. in this extreme case fp ! gives the upper bound fp0 for the smoothing factor s. ! ier=1 : error. the required storage space exceeds the available ! storage space, as specified by the parameter nest. ! probably causes : nest too small. if nest is already ! large (say nest > m/2), it may also indicate that s is ! too small ! the approximation returned is the weighted least-squares ! spline according to the knots t(1),t(2),...,t(n). (n=nest) ! the parameter fp gives the corresponding weighted sum of ! squared residuals (fp>s). ! ier=2 : error. a theoretically impossible result was found during ! the iteration process for finding a smoothing spline with ! fp = s. probably causes : s too small. ! there is an approximation returned but the corresponding ! weighted sum of squared residuals does not satisfy the ! condition abs(fp-s)/s < tol. ! ier=3 : error. the maximal number of iterations maxit (set to 20 ! by the program) allowed for finding a smoothing spline ! with fp=s has been reached. probably causes : s too small ! there is an approximation returned but the corresponding ! weighted sum of squared residuals does not satisfy the ! condition abs(fp-s)/s < tol. ! ier=10 : error. on entry, the input data are controlled on validity ! the following restrictions must be satisfied. ! -1<=iopt<=1, 1<=k<=5, m>k, nest>2*k+2, w(i)>0,i=1,2,...,m ! xb<=x(1)<x(2)<...<x(m)<=xe, lwrk>=(k+1)*m+nest*(7+3*k) ! if iopt=-1: 2*k+2<=n<=min(nest,m+k+1) ! xb<t(k+2)<t(k+3)<...<t(n-k-1)<xe ! the schoenberg-whitney conditions, i.e. there ! must be a subset of data points xx(j) such that ! t(j) < xx(j) < t(j+k+1), j=1,2,...,n-k-1 ! if iopt>=0: s>=0 ! if s=0 : nest >= m+k+1 ! if one of these conditions is found to be violated,control ! is immediately repassed to the calling program. in that ! case there is no approximation returned. ! ! further comments: ! by means of the parameter s, the user can control the tradeoff ! between closeness of fit and smoothness of fit of the approximation. ! if s is too large, the spline will be too smooth and signal will be ! lost ; if s is too small the spline will pick up too much noise. in ! the extreme cases the program will return an interpolating spline if ! s=0 and the weighted least-squares polynomial of degree k if s is ! very large. between these extremes, a properly chosen s will result ! in a good compromise between closeness of fit and smoothness of fit. ! to decide whether an approximation, corresponding to a certain s is ! satisfactory the user is highly recommended to inspect the fits ! graphically. ! recommended values for s depend on the weights w(i). if these are ! taken as 1/d(i) with d(i) an estimate of the standard deviation of ! y(i), a good s-value should be found in the range (m-sqrt(2*m),m+ ! sqrt(2*m)). if nothing is known about the statistical error in y(i) ! each w(i) can be set equal to one and s determined by trial and ! error, taking account of the comments above. the best is then to ! start with a very large value of s ( to determine the least-squares ! polynomial and the corresponding upper bound fp0 for s) and then to ! progressively decrease the value of s ( say by a factor 10 in the ! beginning, i.e. s=fp0/10, fp0/100,...and more carefully as the ! approximation shows more detail) to obtain closer fits. ! to economize the search for a good s-value the program provides with ! different modes of computation. at the first call of the routine, or ! whenever he wants to restart with the initial set of knots the user ! must set iopt=zero ! if iopt=1 the program will continue with the set of knots found at ! the last call of the routine. this will save a lot of computation ! time if curfit is called repeatedly for different values of s. ! the number of knots of the spline returned and their location will ! depend on the value of s and on the complexity of the shape of the ! function underlying the data. but, if the computation mode iopt=1 ! is used, the knots returned may also depend on the s-values at ! previous calls (if these were smaller). therefore, if after a number ! of trials with different s-values and iopt=1, the user can finally ! accept a fit as satisfactory, it may be worthwhile for him to call ! curfit once more with the selected value for s but now with iopt=0. ! indeed, curfit may then return an approximation of the same quality ! of fit but with fewer knots and therefore better if data reduction ! is also an important objective for the user. ! ! other subroutines required: ! fpback,fpbspl,fpchec,fpcurf,fpdisc,fpgivs,fpknot,fprati,fprota ! ! references: ! dierckx p. : an algorithm for smoothing, differentiation and integ- ! ration of experimental data using spline functions, ! j.comp.appl.maths 1 (1975) 165-184. ! dierckx p. : a fast algorithm for smoothing data on a rectangular ! grid while using spline functions, siam j.numer.anal. ! 19 (1982) 1286-1304. ! dierckx p. : an improved algorithm for curve fitting with spline ! functions, report tw54, dept. computer science,k.u. ! leuven, 1981. ! dierckx p. : curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author: ! p.dierckx ! dept. computer science, k.u. leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! creation date : may 1979 ! latest update : march 1987 ! ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: xb,xe,s real(RKIND), intent(inout) :: fp integer, intent(in) :: iopt,m,k,nest,lwrk integer, intent(out) :: ier integer, intent(inout) :: n ! ..array arguments.. real(RKIND), intent(in) :: x(m),y(m),w(m) real(RKIND), intent(inout) :: t(nest),c(nest),wrk(lwrk) integer, intent(inout) :: iwrk(nest) ! ..local scalars.. integer :: i,ia,ib,ifp,ig,iq,iz,j,k1,k2,lwest,nmin ! .. ! we set up the parameters tol and maxit real(RKIND), parameter :: tol = smallnum03 integer , parameter :: maxit = 20 k1 = k+1 k2 = k1+1 nmin = 2*k1 ! before starting computations a data check is made. if the input data ! are invalid, control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR if (k<=0 .or. k>5) return if (iopt<(-1) .or. iopt>1) return if (m<k1 .or. nest<nmin) return lwest = m*k1+nest*(7+3*k) if (lwrk<lwest) return if (xb>x(1) .or. xe<x(m)) return if (any(x(1:m-1)>x(2:m))) return if (iopt>=0) then if (s<zero .or. (equal(s,zero) .and. nest<(m+k1))) return else if (n<nmin .or. n>nest) return j = n do i=1,k1 t(i) = xb t(j) = xe j = j-1 end do ier = fpchec(x,m,t,n,k); if (ier/=0) return endif ier = FITPACK_OK ! we partition the working space and determine the spline approximation. ifp = 1 iz = ifp+nest ia = iz+nest ib = ia+nest*k1 ig = ib+nest*k2 iq = ig+nest*k2 call fpcurf(iopt,x,y,w,m,xb,xe,k,s,nest,tol,maxit,k1,k2,n,t,c,fp, & wrk(ifp),wrk(iz),wrk(ia),wrk(ib),wrk(ig),wrk(iq),iwrk,ier) end subroutine curfit ! function dblint calculates the double integral ! / xe / ye ! | | s(x,y) dx dy ! xb / yb / ! with s(x,y) a bivariate spline of degrees kx and ky, given in the b-spline representation. real(RKIND) function dblint(tx,nx,ty,ny,c,kx,ky,xb,xe,yb,ye,wrk) result(dblint_res) ! ! calling sequence: ! aint = dblint(tx,nx,ty,ny,c,kx,ky,xb,xe,yb,ye,wrk) ! ! input parameters: ! tx : real array, length nx, which contains the position of the knots in the x-direction. ! nx : integer, giving the total number of knots in the x-direction ! ty : real array, length ny, which contains the position of the knots in the y-direction. ! ny : integer, giving the total number of knots in the y-direction ! c : real array, length (nx-kx-1)*(ny-ky-1), which contains the b-spline coefficients. ! kx,ky : integer values, giving the degrees of the spline. ! xb,xe : real values, containing the boundaries of the integration ! yb,ye domain. s(x,y) is considered to be identically zero outside the rectangle ! (tx(kx+1),tx(nx-kx))*(ty(ky+1),ty(ny-ky)) ! ! output parameters: ! aint : real , containing the double integral of s(x,y). ! wrk : real array of dimension at least (nx+ny-kx-ky-2). used as working space. ! on exit, wrk(i) will contain the integral ! / xe ! | ni,kx+1(x) dx , i=1,2,...,nx-kx-1 ! xb / ! with ni,kx+1(x) the normalized b-spline defined on the knots tx(i),...,tx(i+kx+1) ! wrk(j+nx-kx-1) will contain the integral ! / ye ! | nj,ky+1(y) dy , j=1,2,...,ny-ky-1 ! yb / ! with nj,ky+1(y) the normalized b-spline defined on the knots ty(j),...,ty(j+ky+1) ! ! other subroutines required: fpintb ! ! references : ! gaffney p.w. : the calculation of indefinite integrals of b-splines ! j. inst. maths applics 17 (1976) 37-41. ! dierckx p. : curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! ..scalar arguments.. integer, intent(in) :: nx,ny,kx,ky real(RKIND), intent(in) :: xb,xe,yb,ye ! ..array arguments.. real(RKIND), intent(in) :: tx(nx),ty(ny),c((nx-kx-1)*(ny-ky-1)) real(RKIND), intent(out) :: wrk(nx+ny-kx-ky-2) ! ..local scalars.. integer :: i,j,l,m,nkx1,nky1 real(RKIND) :: res ! .. nkx1 = nx-kx-1 nky1 = ny-ky-1 ! we calculate the integrals of the normalized b-splines ni,kx+1(x) call fpintb(tx,nx,wrk,nkx1,xb,xe) ! we calculate the integrals of the normalized b-splines nj,ky+1(y) call fpintb(ty,ny,wrk(nkx1+1),nky1,yb,ye) ! calculate the integral of s(x,y) dblint_res = zero x_dim: do i=1,nkx1 res = wrk(i) if (equal(res,zero)) cycle x_dim m = (i-1)*nky1 l = nkx1 y_dim: do j=1,nky1 m = m+1 l = l+1 dblint_res = dblint_res + res*wrk(l)*c(m) end do y_dim end do x_dim return end function dblint ! function program evapol evaluates the function f(x,y) = s(u,v), defined through the transformation ! x = u*rad(v)*cos(v) y = u*rad(v)*sin(v) ! and where s(u,v) is a bicubic spline ( 0<=u<=1 , -pi<=v<=pi ), given in its standard b-spline ! representation. pure real(RKIND) function evapol(tu,nu,tv,nv,c,rad,x,y) result(e_res) ! calling sequence: ! f = evapol(tu,nu,tv,nv,c,rad,x,y) ! ! input parameters: ! tu : real array, length nu, which contains the position of the knots in the u-direction. ! nu : integer, giving the total number of knots in the u-direction ! tv : real array, length nv, which contains the position of the knots in the v-direction. ! nv : integer, giving the total number of knots in the v-direction ! c : real array, length (nu-4)*(nv-4), which contains the b-spline coefficients. ! rad : real function subprogram, defining the boundary of the approximation domain. must be ! declared external in the calling (sub)-program ! x,y : the co-ordinates of the point where f(x,y) must be evaluated. ! ! output parameter: ! e_res : the value of f(x,y) ! ! other subroutines required: ! bispev,fpbisp,fpbspl ! ! references : ! de boor c : on calculating with b-splines, j. approximation theory 6 (1972) 50-62. ! cox m.g. : the numerical evaluation of b-splines, j. inst. maths applics 10 (1972) 134-149. ! dierckx p. : curve and surface fitting with splines, monographs on numerical analysis, oxford ! university press, 1993. ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! ..scalar arguments.. integer, intent(in) :: nu,nv real(RKIND), intent(in) :: x,y ! ..array arguments.. real(RKIND), intent(in) :: tu(nu),tv(nv),c((nu-4)*(nv-4)) ! ..user specified function procedure(boundary) :: rad ! ..local scalars.. integer :: ier integer, parameter :: liwrk = 2, lwrk = 8 real(RKIND) :: u(1),v(1),r,f(1),dist ! ..local arrays real(RKIND) :: wrk(lwrk) integer :: iwrk(liwrk) ! .. ! calculate the (u,v)-coordinates of the given point. u = zero v = zero dist = x**2+y**2 if (dist>zero) then v(1) = atan2(y,x) r = rad(v(1)) if (r>zero) & u(1) = min(sqrt(dist)/r,one) endif ! evaluate s(u,v) call bispev(tu,nu,tv,nv,c,3,3,u,1,v,1,f,wrk,lwrk,iwrk,liwrk,ier) ! Return scalar result e_res = f(1) return end function evapol ! subroutine fourco calculates the integrals ! /t(n-3) ! ress(i) = ! s(x)*sin(alfa(i)*x) dx and ! t(4)/ ! /t(n-3) ! resc(i) = ! s(x)*cos(alfa(i)*x) dx, i=1,...,m, ! t(4)/ ! where s(x) denotes a cubic spline which is given in its b-spline representation. pure subroutine fourco(t,n,c,alfa,m,ress,resc,wrk1,wrk2,ier) ! calling sequence: ! call fourco(t,n,c,alfa,m,ress,resc,wrk1,wrk2,ier) ! ! input parameters: ! t : real array,length n, containing the knots of s(x). ! n : integer, containing the total number of knots. n>=10. ! c : real array,length n, containing the b-spline coefficients. ! alfa : real array,length m, containing the parameters alfa(i). ! m : integer, specifying the number of integrals to be computed. ! wrk1 : real array,length n. used as working space ! wrk2 : real array,length n. used as working space ! ! output parameters: ! ress : real array,length m, containing the integrals ress(i). ! resc : real array,length m, containing the integrals resc(i). ! ier : error flag: ! ier=0 : normal return. ! ier=10: invalid input data (see restrictions). ! ! restrictions: ! n >= 10 ! t(4) < t(5) < ... < t(n-4) < t(n-3). ! t(1) <= t(2) <= t(3) <= t(4). ! t(n-3) <= t(n-2) <= t(n-1) <= t(n). ! ! other subroutines required: fpbfou,fpcsin ! ! references : ! dierckx p. : calculation of fourier coefficients of discrete functions using cubic splines. ! j. computational and applied mathematics 3 (1977) 207-209. ! dierckx p. : curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! ..scalar arguments.. integer, intent(in) :: n,m integer, intent(out) :: ier ! ..array arguments.. real(RKIND), intent(in) :: t(n),c(n),alfa(m) real(RKIND), intent(inout) :: wrk1(n),wrk2(n) real(RKIND), intent(out) :: ress(m),resc(m) ! ..local scalars.. integer :: i,n4 ! .. n4 = n-4 ! before starting computations a data check is made. in the input data ! are invalid, control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR ! Not enough points if (n<10) return ! Ends of the support: knots must be monotonic if (any(t(1:3)>t(2:4))) return if (any(t(n-2:n)<t(n-3:n-1))) return ! Interior: knots must be strictly monotonic if (any(t(4:n4)>=t(5:n-3))) return ier = FITPACK_OK ! main loop for the different alfa(i). alphas: do i=1,m ! calculate the integrals ! wrk1(j) = integral(nj,4(x)*sin(alfa*x)) and ! wrk2(j) = integral(nj,4(x)*cos(alfa*x)), j=1,2,...,n-4, ! where nj,4(x) denotes the normalised cubic b-spline defined on the knots t(j),t(j+1),...,t(j+4). call fpbfou(t,n,alfa(i),wrk1,wrk2) ! calculate the integrals ress(i) and resc(i). ress(i) = dot_product(c(1:n4),wrk1(1:n4)) resc(i) = dot_product(c(1:n4),wrk2(1:n4)) end do alphas end subroutine fourco ! subroutine fpader calculates the derivatives ! (j-1) ! d(j) = s (x) , j=1,2,...,k1 ! of a spline of order k1 at the point t(l)<=x<t(l+1), using the ! stable recurrence scheme of de boor ! .. pure subroutine fpader(t,n,c,k1,x,l,d) ! ..scalar arguments.. real(RKIND), intent(in) :: x integer, intent(in) :: n,k1,l ! ..array arguments.. real(RKIND), intent(in) :: t(n),c(n) real(RKIND), intent(out) :: d(k1) ! ..local scalars.. integer :: i,ik,j,jj,j1,j2,ki,kj,li,lj,lk real(RKIND) :: ak,fac ! ..local array.. real(RKIND) :: h(20) ! .. lk = l-k1 do i=1,k1 ik = i+lk h(i) = c(ik) end do kj = k1 fac = one order_loop: do j=1,k1 ki = kj j1 = j+1 if(j>1) then i = k1 do jj=j,k1 li = i+lk lj = li+kj h(i) = (h(i)-h(i-1))/(t(lj)-t(li)) i = i-1 end do endif d(j:k1) = h(j:k1) if(j<k1) then do jj=j1,k1 ki = ki-1 i = k1 do j2=jj,k1 li = i+lk lj = li+ki d(i) = ((x-t(li))*d(i)+(t(lj)-x)*d(i-1))/(t(lj)-t(li)) i = i-1 end do end do endif d(j) = d(k1)*fac ak = k1-j fac = fac*ak kj = kj-1 end do order_loop return end subroutine fpader pure subroutine fpadno(maxtr,up,left,right,info,count,merk,jbind,n1,ier) ! subroutine fpadno adds a branch of length n1 to the triply linked tree,the information of ! which is kept in the arrays up,left,right and info. the information field of the nodes of ! this new branch is given in the array jbind. in linking the new branch fpadno takes account ! of the property of the tree that info(k) < info(right(k)) ; info(k) < info(left(k)) ! if necessary the subroutine calls subroutine fpfrno to collect the free nodes of the tree. ! if no computer words are available at that moment, the error parameter ier is set to 1. ! .. ! ..scalar arguments.. integer, intent(in) :: maxtr,n1 integer, intent(inout) :: count,merk integer, intent(out) :: ier ! ..array arguments.. integer, intent(inout) :: up(maxtr),left(maxtr),right(maxtr),info(maxtr) integer, intent(in) :: jbind(n1) ! ..local scalars.. integer :: k,level,point logical :: is_left ! .. point = 1 level = 1 k = left(point) is_left = .true. loop: do while (k/=0 .and. info(max(1,k))-jbind(min(level,n1))<=0) point = k if (info(k)-jbind(level)<0) then k = right(point) is_left = .false. else ! info(k)-jbind(level)==0 level = level+1 k = left(point) is_left = .true. endif end do loop loop2: do while (level<=n1) count = count+1 if (count>maxtr) then call fpfrno(maxtr,up,left,right,info,point,merk,n1,count,ier) if (ier/=FITPACK_OK) return endif info (count) = jbind(level) left (count) = 0 right(count) = k if(is_left) then up (count) = point left (point) = count else is_left = .true. right(point) = count up (count) = up(point) endif point = count level = level+1 k = 0 end do loop2 ! Success! ier = FITPACK_OK end subroutine fpadno ! given a idim-dimensional spline curve of degree k, in its b-spline representation ( knots t(j), ! j=1,...,n , b-spline coefficients c(j), j=1,...,nc) and given also a polynomial curve in its ! b-spline representation ( coefficients cp(j), j=1,...,np), subroutine fpadpo calculates the b-spline ! representation (coefficients c(j),j=1,...,nc) of the sum of the two curves. pure subroutine fpadpo(idim,t,n,c,nc,k,cp,np,cc,t1,t2) ! .. ! ..scalar arguments.. integer, intent(in) :: idim,k,n,nc,np ! ..array arguments.. real(RKIND), intent(in) :: t(n),cp(np) real(RKIND), intent(out) :: c(nc),cc(nc),t1(n),t2(n) ! ..local scalars.. integer :: i,ii,j,jj,k1,l,l1,n1,n2,nk1,nk2 ! .. k1 = k+1 nk1 = n-k1 ! initialization j = 1 l = 1 do jj=1,idim l1 = j do ii=1,k1 cc(l1) = cp(l) l1 = l1+1 l = l+1 end do j = j+n l = l+k1 end do if (nk1/=k1) then n1 = k1*2 j = n l = n1 do i=1,k1 t1(i) = t(i) t1(l) = t(j) l = l-1 j = j-1 end do ! find the b-spline representation of the given polynomial curve ! according to the given set of knots. nk2 = nk1-1 do l=k1,nk2 l1 = l+1 j = 1 do i=1,idim call fpinst(0,t1,n1,cc(j),k,t(l1),l,t2,n2,cc(j),n) j = j+n end do t1(1:n2) = t2(1:n2) n1 = n2 end do endif ! find the b-spline representation of the resulting curve. j = 1 do jj=1,idim l = j do i=1,nk1 c(l) = cc(l)+c(l) l = l+1 end do j = j+n end do return end subroutine fpadpo ! function fpback calculates the solution of the system of equations a*c = z with ! a a n x n upper triangular matrix of bandwidth k. pure function fpback(a,z,n,k,nest) result(c) ! ..scalar arguments.. integer, intent(in) :: n,k,nest ! ..array arguments.. real(RKIND), intent(in) :: a(nest,k),z(n) real(RKIND) :: c(n) ! ..local scalars.. real(RKIND) :: store integer :: i,i1,j,k1,l,m ! .. k1 = k-1 c(n) = z(n)/a(n,1) i = n-1 if (i==0) return rows: do j=2,n store = z(i) i1 = merge(j-1,k1,j<=k1) m = i do l=1,i1 m = m+1 store = store-c(m)*a(i,l+1) end do c(i) = store/a(i,1) i = i-1 end do rows end function fpback ! function fpbacp calculates the solution of the system of equations g * c = z ! with g a n x n upper triangular matrix of the form ! ! a ' ! ! g = ! ' b ! ! ! 0 ' ! ! with b a n x k matrix and a a (n-k) x (n-k) upper triangular matrix of bandwidth k1. pure function fpbacp(a,b,z,n,k,k1,nest) result(c) ! ..scalar arguments.. integer, intent(in) :: n,k,k1,nest ! ..array arguments.. real(RKIND), intent(in) :: a(nest,k1),b(nest,k),z(n) real(RKIND) :: c(n) ! ..local scalars.. integer :: i,i1,j,l,l0,l1,n2 real(RKIND) :: store ! .. n2 = n-k l = n do i=1,k store = z(l) j = k+2-i if (i/=1) then l0 = l do l1=j,k l0 = l0+1 store = store-c(l0)*b(l,l1) end do endif c(l) = store/b(l,j-1) l = l-1 if (l==0) return end do do i=1,n2 store = z(i) l = n2 do j=1,k l = l+1 store = store-c(l)*b(i,j) end do c(i) = store end do i = n2 c(i) = c(i)/a(i,1) if (i==1) return do j=2,n2 i = i-1 store = c(i) i1 = k if (j<=k) i1=j-1 l = i do l0=1,i1 l = l+1 store = store-c(l)*a(i,l0+1) end do c(i) = store/a(i,1) end do return end function fpbacp ! subroutine fpbfou calculates the integrals ! /t(n-3) ! ress(j) = ! nj,4(x)*sin(par*x) dx and ! t(4)/ ! /t(n-3) ! resc(j) = ! nj,4(x)*cos(par*x) dx , j=1,2,...n-4 ! t(4)/ ! where nj,4(x) denotes the cubic b-spline defined on the knots t(j),t(j+1),...,t(j+4). pure subroutine fpbfou(t,n,par,ress,resc) ! calling sequence: ! call fpbfou(t,n,par,ress,resc) ! ! input parameters: ! t : real array,length n, containing the knots. ! n : integer, containing the number of knots. ! par : real, containing the value of the parameter par. ! ! output parameters: ! ress : real array,length n, containing the integrals ress(j). ! resc : real array,length n, containing the integrals resc(j). ! ! restrictions: ! n >= 10, t(4) < t(5) < ... < t(n-4) < t(n-3). ! .. ! ..scalar arguments.. integer, intent(in) :: n real(RKIND), intent(in) :: par ! ..array arguments.. real(RKIND), intent(in) :: t(n) real(RKIND), intent(out) :: ress(n),resc(n) ! ..local scalars.. integer :: i,ic,ipj,is,j,jj,jp1,jp4,k,li,lj,ll,nmj,nm3,nm7 real(RKIND) :: ak,beta,c1,c2,delta,fac,f1,f2,f3,sign,s1,s2,term ! ..local arrays.. real(RKIND) :: co(5),si(5),hs(5),hc(5),rs(3),rc(3) ! .. ! initialization. real(RKIND), parameter :: eps = smallnum08 real(RKIND), parameter :: con1 = 0.5e-01_RKIND real(RKIND), parameter :: con2 = 0.12e+03_RKIND nm3 = n-3 nm7 = n-7 term = merge(six/par,zero,not_equal(par,zero)) beta = par*t(4) co(1) = cos(beta) si(1) = sin(beta) ! calculate the integrals ress(j) and resc(j), j=1,2,3 by setting up a divided difference table. left: do j=1,3 jp1 = j+1 jp4 = j+4 beta = par*t(jp4) co(jp1) = cos(beta) si(jp1) = sin(beta) call fpcsin(t(4),t(jp4),par,si(1),co(1),si(jp1),co(jp1),rs(j),rc(j)) i = 5-j hs(i) = zero hc(i) = zero do jj=1,j ipj = i+jj hs(ipj) = rs(jj) hc(ipj) = rc(jj) end do do jj=1,3 if (i<jj) i = jj k = 5 li = jp4 do ll=i,4 lj = li-jj fac = t(li)-t(lj) hs(k) = (hs(k)-hs(k-1))/fac hc(k) = (hc(k)-hc(k-1))/fac k = k-1 li = li-1 end do end do ress(j) = hs(5)-hs(4) resc(j) = hc(5)-hc(4) end do left ! calculate the integrals ress(j) and resc(j),j=4,5,...,n-7. center: do j=4,nm7 jp4 = j+4 beta = par*t(jp4) co(5) = cos(beta) si(5) = sin(beta) delta = t(jp4)-t(j) ! the way of computing ress(j) and resc(j) depends on the value of beta = par*(t(j+4)-t(j)). beta = delta*par ! if |beta|>1 the integrals are calculated by setting up a divided difference table. if(abs(beta)>one) then hs(1:5) = si(1:5) hc(1:5) = co(1:5) do jj=1,3 k = 5 li = jp4 do ll=jj,4 lj = li-jj fac = par*(t(li)-t(lj)) hs(k) = (hs(k)-hs(k-1))/fac hc(k) = (hc(k)-hc(k-1))/fac k = k-1 li = li-1 end do end do s2 = (hs(5)-hs(4))*term c2 = (hc(5)-hc(4))*term else ! if |beta|<=1 the integrals are calculated by evaluating a series expansion. hs(:4) = par*(t(j+1:j+4)-t(j)) hc(:4) = hs(:4) f3 = con1*sum(hs(1:4)) c1 = fourth s1 = f3 if(abs(f3)>eps) then sign = one fac = con2 k = 5 is = 0 series_coef: do ic=1,20 k = k+1 ak = k fac = fac*ak f1 = zero f3 = zero do i=1,4 f1 = f1+hc(i) f2 = f1*hs(i) hc(i) = f2 f3 = f3+f2 end do f3 = f3*six/fac if (is==0) then sign = -sign is = 1 c1 = c1+f3*sign else is = 0 s1 = s1+f3*sign end if if(abs(f3)<=eps) exit series_coef end do series_coef endif s2 = delta*(co(1)*s1+si(1)*c1) c2 = delta*(co(1)*c1-si(1)*s1) endif ress(j) = s2 resc(j) = c2 co(1:4) = co(2:5) si(1:4) = si(2:5) end do center ! calculate the integrals ress(j) and resc(j),j=n-6,n-5,n-4 by setting ! up a divided difference table. right: do j=1,3 nmj = nm3-j i = 5-j call fpcsin(t(nm3),t(nmj),par,si(4),co(4),si(i-1),co(i-1),rs(j),rc(j)) hc(i:i+j) = [zero,rc(1:j)] hs(i:i+j) = [zero,rs(1:j)] do jj=1,3 if(i<jj) i = jj k = 5 li = nmj do ll=i,4 lj = li+jj fac = t(lj)-t(li) hs(k) = (hs(k-1)-hs(k))/fac hc(k) = (hc(k-1)-hc(k))/fac k = k-1 li = li+1 end do end do ress(nmj) = hs(4)-hs(5) resc(nmj) = hc(4)-hc(5) end do right return end subroutine fpbfou pure subroutine fpbisp(tx,nx,ty,ny,c,kx,ky,x,mx,y,my,z,wx,wy,lx,ly) ! ..scalar arguments.. integer , intent(in) :: nx,ny,kx,ky,mx,my ! ..array arguments.. real(RKIND), intent(in) :: tx(nx),ty(ny),c((nx-kx-1)*(ny-ky-1)),x(mx),y(my) integer , intent(out) :: lx(mx),ly(my) real(RKIND), intent(out) :: wx(mx,kx+1),wy(my,ky+1),z(mx*my) ! ..local variables.. integer :: kx1,ky1,l,l1,m,nkx1,nky1,i,i1,j real(RKIND) :: arg,sp,tb,te,h(MAX_ORDER+1) ! X kx1 = kx+1 nkx1 = nx-kx1 tb = tx(kx1) te = tx(nkx1+1) l = kx1 l1 = l+1 x_array: do i=1,mx arg = x(i) if(arg<tb) arg = tb if(arg>te) arg = te do while (.not.(arg<tx(l1) .or. l==nkx1)) l = l1 l1 = l+1 end do h = fpbspl(tx,nx,kx,arg,l) lx(i) = l-kx1 wx(i,1:kx1) = h(1:kx1) end do x_array ! Y ky1 = ky+1 nky1 = ny-ky1 tb = ty(ky1) te = ty(nky1+1) l = ky1 l1 = l+1 y_array: do i=1,my arg = y(i) if(arg<tb) arg = tb if(arg>te) arg = te do while (.not.(arg<ty(l1) .or. l==nky1)) l = l1 l1 = l+1 end do h = fpbspl(ty,ny,ky,arg,l) ly(i) = l-ky1 wy(i,1:ky1) = h(1:ky1) end do y_array m = 0 do i=1,mx l = lx(i)*nky1 h(1:kx1) = wx(i,1:kx1) do j=1,my l1 = l+ly(j) sp = zero do i1=1,kx1 sp = sp+h(i1)*dot_product(c(l1+1:l1+ky1),wy(j,1:ky1)) l1 = l1+nky1 end do m = m+1 z(m) = sp end do end do return end subroutine fpbisp ! function fpbspl evaluates the (k+1) non-zero b-splines of degree k at t(l) <= x < t(l+1) using ! the stable recurrence relation of de boor and cox. ! Travis Oliphant 2007 changed so that weighting of 0 is used when knots with multiplicity are present. ! Also, notice that l+k <= n and 1 <= l+1-k or else the routine will be accessing memory outside t ! Thus it is imperative that that k <= l <= n-k but this is not checked. pure function fpbspl(t,n,k,x,l) result(h) integer , intent(in) :: n,k,l real(RKIND), intent(in) :: x,t(n) real(RKIND) :: h(MAX_ORDER+1) ! Local variables real(RKIND) :: f,hh(MAX_ORDER+1) integer :: i,j,li,lj h(1) = one do j=1,k hh(1:j) = h(1:j) h(1) = zero do i=1,j li = l+i lj = li-j if (not_equal(t(li),t(lj))) then f = hh(i)/(t(li)-t(lj)) h(i) = h(i)+f*(t(li)-x) h(i+1) = f*(x-t(lj)) else h(i+1) = zero endif end do end do end function fpbspl ! subroutine fpchec verifies the number and the position of the knots t(j),j=1,2,...,n of a spline ! of degree k, in relation to the number and the position of the data points x(i),i=1,2,...,m. ! If all of the following conditions are fulfilled, the error parameter ier is set to zero. if one ! of the conditions is violated, an error flag is returned. pure integer function fpchec(x,m,t,n,k) result(ier) integer, intent(in) :: m,n,k real(RKIND), intent(in) :: x(m),t(n) ! Local variables integer :: i,j,k1,k2,l,nk1,nk2,nk3 real(RKIND) :: tj,tl ! Init sizes k1 = k+1 k2 = k1+1 nk1 = n-k1 nk2 = nk1+1 ier = FITPACK_INPUT_ERROR ! 1) k+1 <= n-k-1 <= m if(nk1<k1 .or. nk1>m) return ! 2) monotonicity ! t(1) <= t(2) <= ... <= t(k+1) ! t(n-k) <= t(n-k+1) <= ... <= t(n) j = n monotonic: do i=1,k if(t(i)>t(i+1)) return if(t(j)<t(j-1)) return j = j-1 end do monotonic ! 3) t(k+1) < t(k+2) < ... < t(n-k) do i=k2,nk2 if(t(i)<=t(i-1)) return end do ! 4) t(k+1) <= x(i) <= t(n-k) ! 5) schoenberg and whitney conditions: they must hold for at least one subset of data points, i.e. ! there must be a subset of data points y(j) such that ! t(j) < y(j) < t(j+k+1), j=1,2,...,n-k-1 if(x(1)<t(k1) .or. x(m)>t(nk2)) return if(x(1)>=t(k2) .or. x(m)<=t(nk1)) return i = 1 l = k2 nk3 = nk1-1 if (nk3>=2) then do j=2,nk3 tj = t(j) l = l+1 tl = t(l) do while (i<m .and. x(i)<=tj) i = i+1 if(i>=m) return end do if (x(i)>=tl) return end do endif ! All checks passed ier = FITPACK_OK end function fpchec pure integer function fpched(x,m,t,n,k,ib,ie) result(ier) ! subroutine fpched verifies the number and the position of the knots t(j),j=1,2,...,n of a spline ! of degree k,with ib derative constraints at x(1) and ie constraints at x(m), in relation to the ! number and the position of the data points x(i),i=1,2,...,m. if all of the following conditions ! are fulfilled, the error parameter ier is set to zero. if one of the conditions is violated ier ! is set to ten. ! .. ! ..scalar arguments.. integer, intent(in) :: m,n,k,ib,ie ! ..array arguments.. real(RKIND), intent(in) :: x(m),t(n) ! ..local scalars.. integer :: i,ib1,ie1,j,jj,k1,k2,l,nk1,nk2,nk3 real(RKIND) :: tj,tl ! .. k1 = k+1 k2 = k1+1 nk1 = n-k1 nk2 = nk1+1 ib1 = max(ib-1,0) ie1 = max(ie-1,0) ier = FITPACK_INPUT_ERROR ! 1) k+1 <= n-k-1 <= m + max(0,ib-1) + max(0,ie-1) if(nk1<k1 .or. nk1>(m+ib1+ie1)) return ! 2) t(1) <= t(2) <= ... <= t(k+1) ! t(n-k) <= t(n-k+1) <= ... <= t(n) j = n do i=1,k if(t(i)>t(i+1)) return if(t(j)<t(j-1)) return j = j-1 end do ! 3) t(k+1) < t(k+2) < ... < t(n-k) do i=k2,nk2 if(t(i)<=t(i-1)) return end do ! 4) t(k+1) <= x(i) <= t(n-k) if(x(1)<t(k1) .or. x(m)>t(nk2)) return ! 5) the conditions specified by schoenberg and whitney must hold for at least one subset ! of data points, i.e. there must be a subset of data points y(j) such that ! t(j) < y(j) < t(j+k+1), j=1+ib1,2+ib1,...,n-k-1-ie1 ! with ib1 = max(0,ib-1), ie1 = max(0,ie-1) if (x(1)>=t(k2) .or. x(m)<=t(nk1)) return i = 1 jj = 2+ib1 l = jj+k nk3 = nk1-1-ie1 if(nk3>=jj) then do j=jj,nk3 tj = t(j) l = l+1 tl = t(l) do i = i+1 if (i>=m) return if (x(i)>tj) exit end do if(x(i)>=tl) return end do endif ! Success! all checks passed ier = FITPACK_OK return end function fpched ! subroutine fpchep verifies the number and the position of the knots t(j),j=1,2,...,n of a ! periodic spline of degree k, in relation to the number and the position of the data points ! x(i),i=1,2,...,m. ! if all of the following conditions are fulfilled, ier is set to zero. ! if one of the conditions is violated ier is set to ten. pure integer function fpchep(x,m,t,n,k) result(ier) ! ..scalar arguments.. integer, intent(in) :: m,n,k ! ..array arguments.. real(RKIND), intent(in) :: x(m),t(n) ! ..local scalars.. integer :: i,i1,i2,j,j1,k1,k2,l,l1,l2,mm,m1,nk1,nk2 real(RKIND) :: per,tj,tl,xi ! .. k1 = k+1 k2 = k1+1 nk1 = n-k1 nk2 = nk1+1 m1 = m-1 ier = FITPACK_INPUT_ERROR ! 1) k+1 <= n-k-1 <= m+k-1 if(nk1<k1 .or. n>m+2*k) return ! 2) t(1) <= t(2) <= ... <= t(k+1) ! t(n-k) <= t(n-k+1) <= ... <= t(n) j = n do i=1,k if (t(i)>t(i+1)) return if (t(j)<t(j-1)) return j = j-1 end do ! 3) t(k+1) < t(k+2) < ... < t(n-k) do i=k2,nk2 if (t(i)<=t(i-1)) return end do ! 4) t(k+1) <= x(i) <= t(n-k) if(x(1)<t(k1) .or. x(m)>t(nk2)) return ! 5) the conditions specified by schoenberg and whitney must hold for at least one subset ! of data points, i.e. there must be a subset of data points y(j) such that ! t(j) < y(j) < t(j+k+1), j=k+1,...,n-k-1 l1 = k1 l2 = 1 outer: do l=1,m xi = x(l) do while (.not.(xi<t(l1+1) .or. l==nk1)) l1 = l1+1 l2 = l2+1 if (l2>k1) exit outer end do end do outer if (l2<=k1) l = m per = t(nk2)-t(k1) subset_start: do i1=2,l i = i1-1 mm = i+m1 subset_inner: do j=k1,nk1 tj = t(j) j1 = j+k1 tl = t(j1) xi = -huge(xi) do while (xi<=tj) i = i+1 if (i>mm) cycle subset_start i2 = i-m1 if (i2<=0) then xi = x(i) else xi = x(i2)+per endif end do if(xi>=tl) cycle subset_start end do subset_inner ! A full Shoenberg-Whitney subset is found ier = FITPACK_OK return end do subset_start ! No subsets found: return with error end function fpchep pure subroutine fpclos(iopt,idim,m,u,mx,x,w,k,s,nest,tol, & maxit,k1,k2,n,t,nc,c,fp,fpint,z,a1,a2,b,g1,g2,q,nrdata,ier) ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: s,tol real(RKIND), intent(inout) :: fp integer, intent(in) :: iopt,idim,m,mx,k,nest,maxit,k1,k2,nc integer, intent(inout) :: n,ier ! ..array arguments.. real(RKIND), intent(in) :: u(m),x(mx),w(m) real(RKIND), intent(inout) :: t(nest),c(nc),fpint(nest),z(nc),a1(nest,k1), & a2(nest,k),b(nest,k2),g1(nest,k2),g2(nest,k1),q(m,k1) integer, intent(inout) :: nrdata(nest) ! ..local scalars.. real(RKIND) :: acc,cos,d1,fac,fpart,fpms,fpold,fp0,f1,f2,f3,p,per,pinv,piv,p1,p2,p3,sin,store,& term,ui,wi,rn integer :: i,ij,ik,it,iter,i1,i2,j,jj,jk,jper,j1,j2,kk,kk1,k3,l,l0,l1,l5,mm,m1,new,& nk1,nk2,nmax,nmin,nplus,npl1,nrint,n10,n11,n7,n8 ! ..local arrays.. real(RKIND) :: h(MAX_ORDER+1),h1(7),h2(6),xi(MAX_IDIM) logical :: done,check1,check3,success fpold = zero fp0 = zero nplus = 0 ! ********************************************************************************************** ! part 1: determination of the number of knots and their position ! ********************************************************************************************** ! given a set of knots we compute the least-squares closed curve sinf(u). if the sum ! f(p=inf) <= s we accept the choice of knots. ! if iopt=-1 sinf(u) is the requested curve ! if iopt=0 or iopt=1 we check whether we can accept the knots: ! if fp <=s we will continue with the current set of knots. ! if fp > s we will increase the number of knots and compute the orresponding least-squares ! curve until finally fp<=s. ! the initial choice of knots depends on the value of s and iopt. if s=0 we have spline ! interpolation; in that case the number of knots equals nmax = m+2*k. c ! if s > 0 and iopt=0 we first compute the least-squares polynomial curve of degree k; ! n = nmin = 2*k+2. since s(u) must be periodic, s(u) reduces to a fixed point. c ! if iopt=1 we start with the set of knots found at the last call of the routine, except for ! the case that s > fp0; then we compute directly the least-squares polynomial curve. ! ********************************************************************************************** m1 = m-1 kk = k kk1 = k1 k3 = 3*k+1 nmin = 2*k1 ! determine the length of the period of the splines. per = u(m)-u(1) if (iopt>=0) then ! calculation of acc, the absolute tolerance for the root of f(p)=s. acc = tol*s ! determine nmax, the number of knots for periodic spline interpolation nmax = m+2*k interp_or_fit: if (s<=zero .and. nmax/=nmin) then ! if s=0, s(u) is an interpolating curve. n = nmax ! test whether the required storage space exceeds the available one. if (n>nest) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! find the position of the interior knots in case of interpolation. call fpclos_reset_interp(idim,k,m,mx,n,nc,nest,kk,kk1,u,x,t,c,fp,per,fp0,s,fpint,nrdata,done) if (done) then ier = FITPACK_INTERPOLATING_OK return endif else interp_or_fit ! if s > 0 our initial choice depends on the value of iopt. ! if iopt=0 or iopt=1 and s>=fp0, we start computing the least-squares ! polynomial curve. (i.e. a constant point). ! if iopt=1 and fp0>s we start computing the least-squares closed ! curve according the set of knots found at the last call of the ! routine. if (iopt/=0 .and. n/=nmin) then fp0 = fpint(n) fpold = fpint(n-1) nplus = nrdata(n) endif ! the case that s(u) is a fixed point is treated separetely. ! fp0 denotes the corresponding sum of squared residuals. if (iopt==0 .or. (iopt/=0 .and. s>=fp0)) then fp0 = zero d1 = zero z(1:idim) = zero jj = 0 do it=1,m1 wi = w(it) call fpgivs(wi,d1,cos,sin) do j=1,idim jj = jj+1 fac = wi*x(jj) call fprota(cos,sin,fac,z(j)) fp0 = fp0+fac**2 end do end do z(1:idim) = z(1:idim)/d1 ! test whether that fixed point is a solution of our problem. fpms = fp0-s is_constant: if (fpms<acc .or. nmax==nmin) then ier = FITPACK_LEASTSQUARES_OK ! the point (z(1),z(2),...,z(idim)) is a solution of our problem. ! a constant function is a spline of degree k with all b-spline ! coefficients equal to that constant. do i=1,k1 rn = k1-i t(i) = u(1)-rn*per j = i+k1 rn = i-1 t(j) = u(m)+rn*per end do n = nmin forall(j=1:idim,i=1:k1) c(n*(j-1)+i) = z(j) fp = fp0 fpint(n-1:n) = [zero,fp0] nrdata(n) = 0 return end if is_constant fpold = fp0 ! test whether the required storage space exceeds the available one. if (n>=nest) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! start computing the least-squares closed curve with one interior knot. nplus = 1 n = nmin+1 mm = (m+1)/2 t(k2) = u(mm) nrdata(1) = mm-2 nrdata(2) = m1-mm endif endif interp_or_fit endif ! main loop for the different sets of knots. m is a save upper ! bound for the number of trials. iter = 0 find_knots: do while (iter<m) ! Increment iteration iter = iter+1 ! find nrint, the number of knot intervals. nrint = n-nmin+1 ! find the position of the additional knots which are needed for ! the b-spline representation of s(u). if we take ! t(k+1) = u(1), t(n-k) = u(m) ! t(k+1-j) = t(n-k-j) - per, j=1,2,...k ! t(n-k+j) = t(k+1+j) + per, j=1,2,...k ! then s(u) will be a smooth closed curve if the b-spline ! coefficients satisfy the following conditions ! c((i-1)*n+n7+j) = c((i-1)*n+j), j=1,...k,i=1,2,...,idim (**) ! with n7=n-2*k-1. t(k1) = u(1) nk1 = n-k1 nk2 = nk1+1 t(nk2) = u(m) do j=1,k i1 = nk2+j i2 = nk2-j j1 = k1+j j2 = k1-j t(i1) = t(j1)+per t(j2) = t(i2)-per end do ! compute the b-spline coefficients of the least-squares closed curve sinf(u). the ! observation matrix a is built up row by row while taking into account condition (**) ! and is reduced to triangular form by givens transformations. ! at the same time fp=f(p=inf) is computed. ! the n7 x n7 triangularised upper matrix a has the form ! ! a1 ' ! ! a = ! ' a2 ! ! ! 0 ' ! ! with a2 a n7 x k matrix and a1 a n10 x n10 upper triangular matrix of bandwidth k+1 ( ! (n10 = n7-k). ! initialization. z(1:nc) = zero a1(1:nk1,1:kk1) = zero n7 = nk1-k n10 = n7-kk jper = 0 fp = zero l = k1 get_coefs: do it=1,m1 ! fetch the current data point u(it),x(it) ui = u(it) wi = w(it) xi(1:idim) = wi*x(idim*(it-1)+1:idim*it) ! search for knot interval t(l) <= ui < t(l+1). do while (ui>=t(l+1)) l = l+1 end do ! evaluate the (k+1) non-zero b-splines at ui and store them in q. h = fpbspl(t,n,k,ui,l) q(it,1:k1) = h(1:k1) h(1:k1) = h(1:k1)*wi ! test whether the b-splines nj,k+1(u),j=1+n7,...nk1 are all zero at ui l5 = l-k1 all_zero: if (l5>=n10) then ! initialize the matrix a2. if (jper==0) then a2(1:n7,1:kk) = zero jk = n10+1 do i=1,kk ik = jk do j=1,kk1 if (ik<=0) exit a2(ik,i) = a1(ik,j) ik = ik-1 end do jk = jk+1 end do jper = 1 endif ! if one of the b-splines nj,k+1(u),j=n7+1,...nk1 is not zero at ui we take account ! of condition (**) for setting up the new row of the observation matrix a. this row ! is stored in the arrays h1 (the part with respect to a1) and h2 (the part with ! respect to a2). h1(:kk1) = zero h2(:kk) = zero j = l5-n10 do i=1,kk1 j = j+1 l0 = j l1 = l0-kk do while (l1>max(0,n10)) l0 = l1-n10 l1 = l0-kk end do if (l1>0) then h1(l1) = h(i) else h2(l0) = h2(l0)+h(i) end if end do ! rotate the new row of the observation matrix into triangle ! by givens transformations. if (n10>0) then ! rotation with the rows 1,2,...n10 of matrix a. one_to_n10: do j=1,n10 piv = h1(1) if (equal(piv,zero)) then h1(1:kk1) = [h1(2:kk1),zero] else ! calculate the parameters of the givens transformation. call fpgivs(piv,a1(j,1),cos,sin) ! transformation to the right hand side. call fprota(cos,sin,xi(1:idim),z(j:j+(idim-1)*n:n)) ! transformations to the left hand side with respect to a2. call fprota(cos,sin,h2(1:kk),a2(j,1:kk)) if (j==n10) exit one_to_n10 i2 = min(n10-j,kk)+1 ! transformations to the left hand side with respect to a1. call fprota(cos,sin,h1(2:i2),a1(j,2:i2)) h1(1:i2) = [h1(2:i2),zero] endif end do one_to_n10 endif ! n10>0 ! rotation with the rows n10+1,...n7 of matrix a. n10_to_n7: do j=1,kk ij = n10+j piv = h2(j) if (ij<=0 .or. equal(piv,zero)) cycle n10_to_n7 ! calculate the parameters of the givens transformation. call fpgivs(piv,a2(ij,j),cos,sin) ! transformations to right hand side. call fprota(cos,sin,xi(1:idim),z(ij:ij+(idim-1)*n:n)) if (j==kk) exit n10_to_n7 ! transformations to left hand side. j1 = j+1 call fprota(cos,sin,h2(j1:kk),a2(ij,j1:kk)) end do n10_to_n7 else all_zero ! rotation of the new row of the observation matrix into triangle in case ! the b-splines nj,k+1(u),j=n7+1,...n-k-1 are all zero at ui. j = l5 rot_zero: do i=1,kk1 j = j+1 piv = h(i) if (equal(piv,zero)) cycle rot_zero ! calculate the parameters of the givens transformation. call fpgivs(piv,a1(j,1),cos,sin) ! transformations to right hand side. call fprota(cos,sin,xi(1:idim),z(j:j+(idim-1)*n:n)) if (i<kk1) call fprota(cos,sin,h(i+1:kk1),a1(j,2:1+kk1-i)) end do rot_zero endif all_zero ! add contribution of this row to the sum of squares of residual ! right hand sides. fp = fp + sum(xi(1:idim)**2) end do get_coefs fpint(n-1:n) = [fpold,fp0] nrdata(n) = nplus ! backward substitution to obtain the b-spline coefficients . j1 = 1 do j2=1,idim c(j1:j1+n7-1) = fpbacp(a1,a2,z(j1),n7,kk,kk1,nest) j1 = j1+n end do ! calculate from condition (**) the remaining coefficients. do i=1,k j1 = i do j=1,idim j2 = j1+n7 c(j2) = c(j1) j1 = j1+n end do end do ! test whether the approximation sinf(u) is an acceptable solution. fpms = fp-s if (iopt<0 .or. abs(fpms)<acc) return ! if f(p=inf)<s accept the choice of knots. if (fpms<zero) exit find_knots ! if n=nmax, sinf(u) is an interpolating curve. if (n==nmax) then ier = FITPACK_INTERPOLATING_OK return end if ! increase the number of knots. ! if n=nest we cannot increase the number of knots because of the ! storage capacity limitation. if (n==nest) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! determine the number of knots nplus we are going to add. rn = nplus npl1 = merge(int(rn*fpms/(fpold-fp)),nplus*2,fpold-fp>acc) nplus = min(nplus*2,max(npl1,nplus/2,1)) fpold = fp ! compute the sum of squared residuals for each knot interval ! t(j+k) <= ui <= t(j+k+1) and store it in fpint(j),j=1,2,...nrint. fpart = zero i = 1 l = k1 jj = 0 do it=1,m1 if (u(it)>=t(l)) then new = 1 l = l+1 endif term = zero l0 = l-k2 do j2=1,idim fac = zero j1 = l0 do j=1,k1 j1 = j1+1 fac = fac+c(j1)*q(it,j) end do jj = jj+1 term = term+(w(it)*(fac-x(jj)))**2 l0 = l0+n end do fpart = fpart+term if (new/=0) then if (l>k2) then store = term*half fpint(i) = fpart-store i = i+1 fpart = store else fpint(nrint) = term end if new = 0 endif end do fpint(nrint) = fpint(nrint)+fpart add_new_knots: do l=1,nplus ! add a new knot call fpknot(u,m,t,n,fpint,nrdata,nrint,nest,1) ! if n=nmax we locate the knots as for interpolation if (n==nmax) then call fpclos_reset_interp(idim,k,m,mx,n,nc,nest,kk,kk1,u,x,t,c,fp,per,fp0,s,fpint,nrdata,done) if (done) then ier = FITPACK_INTERPOLATING_OK return endif ! Restart iteration iter = 0 cycle find_knots endif ! test whether we cannot further increase the number of knots. if (n==nest) exit add_new_knots end do add_new_knots ! restart the computations with the new set of knots. end do find_knots ! ***** ! part 2: determination of the smoothing closed curve sp(u). ! ***** ! we have determined the number of knots and their position. ! we now compute the b-spline coefficients of the smoothing curve sp(u). the observation matrix ! a is extended by the rows of matrix b expressing that the kth derivative discontinuities of ! sp(u) at the interior knots t(k+2),...t(n-k-1) must be zero. the corresponding weights of ! these additional rows are set to 1/p. iteratively we then have to determine the value of p ! such that f(p), the sum of squared residuals be = s. we already know that the least-squares ! polynomial curve corresponds to p=0, and that the least-squares periodic spline curve ! corresponds to p=infinity. the iteration process which is proposed here, makes use of rational ! interpolation. since f(p) is a convex and strictly decreasing function of p, it can be ! approximated by a rational function r(p) = (u*p+v)/(p+w). three values of p(p1,p2,p3) with ! corresponding values of f(p) (f1=f(p1)-s,f2=f(p2)-s,f3=f(p3)-s) are used to calculate the new ! value of p such that r(p)=s. convergence is guaranteed by taking f1>0 and f3<zero ! ***** ! evaluate the discontinuity jump of the kth derivative of the b-splines at the knots ! t(l),l=k+2,...n-k-1 and store in b. call fpdisc(t,n,k2,b,nest) ! initial value for p. p1 = zero f1 = fp0-s p3 = -one f3 = fpms n11 = n10-1 n8 = n7-1 p = zero l = n7 do i=1,k j = k+1-i p = p+a2(l,j) l = l-1 if (l==0) exit end do if (l>0) p = p + sum(a1(1:n10,1)) rn = n7 p = rn/p check1 = .false. check3 = .false. ! iteration process to find the root of f(p) = s. find_root: do iter=1,maxit ! form the matrix g as the matrix a extended by the rows of matrix b. ! the rows of matrix b with weight 1/p are rotated into ! the triangularised observation matrix a. ! after triangularisation our n7 x n7 matrix g takes the form ! ! g1 ' ! ! g = ! ' g2 ! ! ! 0 ' ! ! with g2 a n7 x (k+1) matrix and g1 a n11 x n11 upper triangular ! matrix of bandwidth k+2. ( n11 = n7-k-1) pinv = one/p ! store matrix a into g c(:nc) = z(:nc) g1(1:n7,1:k1) = a1(1:n7,1:k1) g1(1:n7,k2) = zero g2(1:n7,1) = zero g2(1:n7,2:k1) = a2(1:n7,1:k) l = n10 do j=1,k1 if (l<=0) exit g2(l,1) = a1(l,j) l = l-1 end do n8_rows: do it=1,n8 ! fetch a new row of matrix b and store it in the arrays h1 (the part ! with respect to g1) and h2 (the part with respect to g2). xi(:idim) = zero h1(:k2) = zero h2(:k1) = zero if (it<=n11) then l = it l0 = it storeh2: do j=1,k2 if (l0==n10) then l0 = 1 do l1=j,k2 h2(l0) = b(it,l1)*pinv l0 = l0+1 end do exit storeh2 end if h1(j) = b(it,j)*pinv l0 = l0+1 end do storeh2 else ! it>n11 l = 1 i = it-n10 do j=1,k2 i = i+1 l0 = i l1 = l0-k1 do while (l1>max(0,n11)) l0 = l1-n11 l1 = l0-k1 end do if (l1>0) then h1(l1) = b(it,j)*pinv else h2(l0) = h2(l0)+b(it,j)*pinv endif end do endif ! rotate this row into triangle by givens transformations ! rotation with the rows l,l+1,...n11. rot_n11: do j=l,n11 piv = h1(1) ! calculate the parameters of the givens transformation. call fpgivs(piv,g1(j,1),cos,sin) ! transformation to right hand side. call fprota(cos,sin,xi(1:idim),c(j:j+(idim-1)*n:n)) ! transformation to the left hand side with respect to g2. call fprota(cos,sin,h2(1:k1),g2(j,1:k1)) if (j==n11) exit rot_n11 ! transformation to the left hand side with respect to g1. i2 = min(n11-j,k1)+1 call fprota(cos,sin,h1(2:i2),g1(j,2:i2)) h1(1:i2) = [h1(2:i2),zero] end do rot_n11 ! rotation with the rows n11+1,...n7 rot_n10_n7: do j=1,k1 ij = n11+j if (ij<=0) cycle rot_n10_n7 piv = h2(j) ! calculate the parameters of the givens transformation call fpgivs(piv,g2(ij,j),cos,sin) ! transformation to the right hand side. call fprota(cos,sin,xi(1:idim),c(ij:ij+(idim-1)*n:n)) if (j<k1) then ! transformation to the left hand side. j1 = j+1 call fprota(cos,sin,h2(j1:k1),g2(ij,j1:k1)) endif end do rot_n10_n7 end do n8_rows ! backward substitution to obtain the b-spline coefficients j1 = 1 do j2=1,idim c(j1:j1+n7-1) = fpbacp(g1,g2,c(j1),n7,k1,k2,nest) j1 = j1+n end do ! calculate from condition (**) the remaining b-spline coefficients. do i=1,k j1 = i do j=1,idim j2 = j1+n7 c(j2) = c(j1) j1 = j1+n end do end do ! computation of f(p). fp = zero l = k1 jj = 0 do it=1,m1 if (u(it)>=t(l)) l = l+1 l0 = l-k2 term = zero do j2=1,idim fac = zero j1 = l0 do j=1,k1 j1 = j1+1 fac = fac+c(j1)*q(it,j) end do jj = jj+1 term = term+(fac-x(jj))**2 l0 = l0+n end do fp = fp+term*w(it)**2 end do ! test whether the approximation sp(u) is an acceptable solution. fpms = fp-s if (abs(fpms)<acc) return ! find the new value of p and carry out one more step. call root_finding_iterate(p1,f1,p2,f2,p3,f3,p,fpms,acc,check1,check3,success) if (.not.success) then ier = FITPACK_S_TOO_SMALL return end if end do find_root ! Too many iterations ier = FITPACK_MAXIT return end subroutine fpclos pure subroutine fpclos_reset_interp(idim,k,m,mx,n,nc,nest,kk,kk1,u,x,t,c,fp,per,fp0,s,fpint,nrdata,done) integer, intent(in) :: idim,k,m,mx,n,nc,nest integer, intent(inout) :: kk,kk1 real(RKIND), intent(in) :: u(m),x(mx),per,fp0,s real(RKIND), intent(inout) :: t(nest),c(nc),fp,fpint(nest) integer, intent(inout) :: nrdata(nest) logical, intent(out) :: done integer :: i,j,j1,jj,m1 m1 = m-1 done = .false. k_is_odd: if (mod(k,2)/=0) then t(k+2:k+m1) = u(2:m1) if (s<=zero) then kk = k-1 kk1 = k if (kk<=0) then t(1:2) = [t(m)-per,u(1)] t(m+1:m+2) = [u(m),t(3)+per] jj = 0 do i=1,m1 j = i do j1=1,idim jj = jj+1 c(j) = x(jj) j = j+n end do end do jj = 1 j = m do j1=1,idim c(j) = c(jj) j = j+n jj = jj+n end do fp = zero fpint(n-1:n) = [zero,fp0] nrdata(n) = 0 done = .true. endif endif else k_is_odd t(k+2:k+m1) = half*(u(2:m1)+u(1:m1-1)) endif k_is_odd end subroutine fpclos_reset_interp ! TODO! output flags in fpcoco and cocosp do not match the global error messages pure subroutine fpcoco(iopt,m,x,y,w,v,s,nest,maxtr,maxbin,n,t,c,sq,sx,bind,e,wrk,lwrk,iwrk,kwrk,ier) ! ..scalar arguments.. real(RKIND), intent(in) :: s real(RKIND), intent(inout) :: sq integer, intent(in) :: iopt,m,nest,maxtr,maxbin,lwrk,kwrk integer, intent(inout) :: n integer, intent(out) :: ier ! ..array arguments.. integer, intent(inout) :: iwrk(kwrk) real(RKIND), intent(in) :: x(m),y(m),w(m),v(m) real(RKIND), intent(inout) :: t(nest),c(nest),sx(m),e(nest),wrk(lwrk) logical, intent(inout) :: bind(nest) ! ..local scalars.. integer :: i,ia,ib,ic,iq,it,iu,iz,izz,i1,j,k,l,l1,m1,nmax,nr,n4,n6,n8,ji,jib,jjb,jl,jr,ju,mb,nm real(RKIND) :: sql,sqmax,term,tj,xi ! ..subroutine references.. ! fpcosp,fpbspl,fpadno,fpdeno,fpseno,fpfrno ! .. ! determine the maximal admissible number of knots. nmax = m+4 ier = FITPACK_OK ! the initial choice of knots depends on the value of iopt. ! if iopt=0 the program starts with the minimal number of knots so that can be guarantied ! that the concavity/convexity constraints will be satisfied. ! if iopt = 1 the program will continue from the point on where it left at the foregoing call. if (iopt<=0) then ! find the minimal number of knots. ! a knot is located at the data point x(i), i=2,3,...m-1 if ! 1) v(i) ^= 0 and ! 2) v(i)*v(i-1) <= 0 or v(i)*v(i+1) <= zero m1 = m-1 n = 4 do i=2,m1 if (equal(v(i),zero) .or. (v(i)*v(i-1)>zero .and. v(i)*v(i+1)>zero)) cycle n = n+1 ! test whether the required storage space exceeds the available one. if (n+4>nest) then ier = FITPACK_TOO_MANY_KNOTS return end if t(n) = x(i) end do ! find the position of the knots t(1),...t(4) and t(n-3),...t(n) which ! are needed for the b-spline representation of s(x). t(1:4) = x(1) t(n+1:n+4) = x(m) n = n+4 ! test whether the minimum number of knots exceeds the maximum number. if (n>nmax) then ier = FITPACK_OVERLAPPING_KNOTS return endif endif ! main loop for the different sets of knots. ! find corresponding values e(j) to the knots t(j+3),j=1,2,...n-6 ! e(j) will take the value -1,1, or 0 according to the requirement ! that s(x) must be locally convex or concave at t(j+3) or that the ! sign of s''(x) is unrestricted at that point. update_knots: do it=1,2*nest if (iopt<=0 .or. it>1) then i = 1 xi = x(1) j = 4 tj = t(4) n6 = n-6 do l=1,n6 do while (not_equal(xi,tj)) i = i+1 xi = x(i) end do e(l) = v(i) j = j+1 tj = t(j) end do ! we partition the working space nm = n+maxbin mb = maxbin+1 ia = 1 ib = ia+4*n ic = ib+nm*maxbin iz = ic+n izz = iz+n iu = izz+n iq = iu+maxbin ji = 1 ju = ji+maxtr jl = ju+maxtr jr = jl+maxtr jjb = jr+maxtr jib = jjb+mb ! given the set of knots t(j),j=1,2,...n, find the least-squares cubic ! spline which satisfies the imposed concavity/convexity constraints. call fpcosp(m,x,y,w,n,t,e,maxtr,maxbin,c,sq,sx,bind,nm,mb,wrk(ia), & wrk(ib),wrk(ic),wrk(iz),wrk(izz),wrk(iu),wrk(iq),iwrk(ji), & iwrk(ju),iwrk(jl),iwrk(jr),iwrk(jjb),iwrk(jib),ier) ! if sq <= s or in case of abnormal exit from fpcosp, control is ! repassed to the driver program. if (sq<=s .or. .not.FITPACK_SUCCESS(ier)) return endif ! calculate for each knot interval t(l-1) <= xi <= t(l) the sum((wi*(yi-s(xi)))**2). ! find the interval t(k-1) <= x <= t(k) for which this sum is maximal on the condition that ! this interval contains at least one interior data point x(nr) and that s(x) is not given ! there by a straight line. sqmax = zero sql = zero l = 5 nr = 0 i1 = 1 n4 = n-4 get_error: do i=1,m term = (w(i)*(sx(i)-y(i)))**2 if (x(i)>=t(l) .and. l<=n4) then term = term*half sql = sql+term if (i-i1>1 .and. any(.not.bind(l-4:l-3)) .and. sql>sqmax) then k = l sqmax = sql nr = i1+(i-i1)/2 endif l = l+1 i1 = i sql = zero endif sql = sql+term end do get_error if (m-i1>1 .and. any(.not.bind(l-4:l-3)) .and. sql>sqmax) then k = l nr = i1+(m-i1)/2 endif ! if no such interval is found, control is repassed to the driver program (ier = -1). if (nr==0) then ier = FITPACK_INTERPOLATING_OK return end if ! if s(x) is given by the same straight line in two succeeding knot ! intervals t(l-1) <= x <= t(l) and t(l) <= x <= t(l+1),delete t(l) n8 = n-8 l1 = 0 if (n8>0) then do i=1,n8 if (all(bind(i:i+2))) then l = i+4-l1 if (k>l) k = k-1 n = n-1 l1 = l1+1 t(l:n) = t(l+1:n+1) endif end do endif ! test whether we cannot further increase the number of knots. if (n==nmax) then ier = FITPACK_LEASTSQUARES_OK return elseif (n==nest) then ier = -3 ! what is this? return end if ! locate an additional knot at the point x(nr). j = n do i=k,n t(j+1) = t(j) j = j-1 end do t(k) = x(nr) n = n+1 ! restart the computations with the new set of knots. end do update_knots ier = FITPACK_MAXIT end subroutine fpcoco subroutine fpcons(iopt,idim,m,u,mx,x,w,ib,ie,k,s,nest, & tol,maxit,k1,k2,n,t,nc,c,fp,fpint,z,a,b,g,q,nrdata,ier) !cc c XXX: mmnin/nmin variables on line 61 ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: s,tol real(RKIND), intent(inout) :: fp integer, intent(in) :: ib,ie,iopt,idim,k,k1,k2,m,mx,maxit,nc,nest integer, intent(inout) :: n,ier ! ..array arguments.. real(RKIND), intent(in) :: u(m),x(mx),w(m) real(RKIND), intent(inout) :: t(nest),c(nc),fpint(nest),z(nc),a(nest,k1),b(nest,k2),g(nest,k2),q(m,k1) integer, intent(inout) :: nrdata(nest) ! ..local scalars.. real(RKIND) :: acc,cos,fac,fpart,fpms,fpold,fp0,f1,f2,f3,p,pinv,piv,p1,p2,p3,rn,sin,store,term,ui,wi integer :: i,it,iter,i1,i2,i3,j,jb,je,jj,j1,j2,j3,kbe,l,li,lj,l0,mb,me,mm,nk1,& nmax,nmin,nn,nplus,npl1,nrint,n8,mmin logical :: new,check1,check3,success ! ..local arrays.. real(RKIND) :: h(MAX_ORDER+1),xi(MAX_IDIM) fpold = zero fp0 = zero nplus = 0 ! ****************************************************************************************** ! part 1: determination of the number of knots and their position ! ****************************************************************************************** ! given a set of knots we compute the least-squares curve sinf(u), and the corresponding sum of ! squared residuals fp=f(p=inf). ! if iopt=-1 sinf(u) is the requested curve. ! if iopt=0 or iopt=1 we check whether we can accept the knots: ! if fp <=s we will continue with the current set of knots. ! if fp > s we will increase the number of knots and compute the corresponding least-squares ! curve until finally fp<=s. the initial choice of knots depends on the value of s and iopt. ! if s=0 we have spline interpolation; in that case the number of knots equals ! nmax = m+k+1-max(0,ib-1)-max(0,ie-1) ! if s > 0 and ! iopt=0 we first compute the least-squares polynomial curve of degree k; n = nmin = 2*k+2 ! iopt=1 we start with the set of knots found at the last call of the routine, except for ! the case that s > fp0; then we compute directly the polynomial curve of degree k. ! ****************************************************************************************** ! determine nmin, the number of knots for polynomial approximation. nmin = 2*k1 ! find which data points are to be considered. mb = merge(2,1,ib>0) jb = merge(ib,1,ib>0) me = merge(m-1,m,ie>0) je = merge(ie,1,ie>0) bootstrap: if (iopt>=0) then ! calculation of acc, the absolute tolerance for the root of f(p)=s. acc = tol*s ! determine nmax, the number of knots for spline interpolation. kbe = k1-jb-je mmin = kbe+2 mm = m-mmin nmax = nmin+mm interpolating: if (s<=zero) then ! if s=0, s(u) is an interpolating curve. ! test whether the required storage space exceeds the available one. n = nmax if (nmax>nest) then ier = FITPACK_INSUFFICIENT_STORAGE return endif ! find the position of the interior knots in case of interpolation. if (mm/=0) then i = k2 j = 3-jb+k/2 do l=1,mm t(i) = u(j) i = i+1 j = j+1 end do endif else interpolating ! if s>0 our initial choice of knots depends on the value of iopt. ! if iopt=0 or iopt=1 and s>=fp0, we start computing the least-squares ! polynomial curve which is a spline curve without interior knots. ! if iopt=1 and fp0>s we start computing the least squares spline curve ! according to the set of knots found at the last call of the routine. use_last_call: if (iopt/=0 .and. n/=nmin) then fp0 = fpint(n) fpold = fpint(n-1) nplus = nrdata(n) if (fp0<=s) then n = nmin fpold = zero nplus = 0 nrdata(1) = m-2 end if else use_last_call n = nmin fpold = zero nplus = 0 nrdata(1) = m-2 endif use_last_call endif interpolating endif bootstrap ! main loop for the different sets of knots. m is a save upper bound ! for the number of trials. iter = 0 main_loop: do while (iter<m) iter = iter+1 if (n==nmin) ier = FITPACK_LEASTSQUARES_OK ! find nrint, tne number of knot intervals. nrint = n-nmin+1 ! find the position of the additional knots which are needed for ! the b-spline representation of s(u). nk1 = n-k1 t(1:k1) = u(1) t(nk1+1:n) = u(m) ! compute the b-spline coefficients of the least-squares spline curve ! sinf(u). the observation matrix a is built up row by row and ! reduced to upper triangular form by givens transformations. ! at the same time fp=f(p=inf) is computed. fp = zero ! nn denotes the dimension of the splines nn = nk1-ib-ie ! initialize the b-spline coefficients and the observation matrix a. z(1:nc) = zero c(1:nc) = zero if (me>=mb) then if (nn>0) a(1:nn,1:k1) = zero l = k1 jj = (mb-1)*idim coefs: do it=mb,me ! fetch the current data point u(it),x(it). ui = u(it) wi = w(it) xi(1:idim) = wi*x(idim*(it-1)+1:idim*it) ! search for knot interval t(l) <= ui < t(l+1). do while (ui>=t(l+1) .and. l/=nk1) l = l+1 end do ! evaluate the (k+1) non-zero b-splines at ui and store them in q. h = fpbspl(t,n,k,ui,l) q(it,1:k1) = h(1:k1) h(:k1) = wi*h(:k1) ! take into account that certain b-spline coefficients must be zero. lj = k1 j = nk1-l-ie if (j<0) lj = lj+j li = 1 j = l-k1-ib if (j<0) then li = li-j j = 0 endif ! rotate the new row of the observation matrix into triangle. rotate_row: do i=li,lj j = j+1 piv = h(i) if (equal(piv,zero)) cycle rotate_row ! calculate the parameters of the givens transformation. call fpgivs(piv,a(j,1),cos,sin) ! transformations to right hand side. call fprota(cos,sin,xi(1:idim),z(j:j+n*(idim-1):n)) ! transformations to left hand side. not_last: if (i<lj) then i2 = 1 i3 = i+1 do i1 = i3,lj i2 = i2+1 call fprota(cos,sin,h(i1),a(j,i2)) end do endif not_last end do rotate_row ! add contribution of this row to the sum of squares of residual right hand sides. fp = fp + sum(xi(1:idim)**2) end do coefs if (ier==FITPACK_LEASTSQUARES_OK) fp0 = fp fpint(n-1:n) = [fpold,fp0] nrdata(n) = nplus ! backward substitution to obtain the b-spline coefficients. if (nn/=0) then j1 = 1 do j2=1,idim j3 = j1+ib c(j3:j3+nn-1) = fpback(a,z(j1),nn,k1,nest) j1 = j1+n end do endif endif ! me>=mb ! test whether the approximation sinf(u) is an acceptable solution. if (iopt<0) return ! was done already fpms = fp-s; if(abs(fpms)<acc) return ! if f(p=inf) < s accept the choice of knots. if (fpms<zero) exit main_loop ! if n = nmax, sinf(u) is an interpolating spline curve. if (n==nmax) then ier = FITPACK_INTERPOLATING_OK return end if ! increase the number of knots. ! if n=nest we cannot increase the number of knots because of ! the storage capacity limitation. if (n==nest) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! determine the number of knots nplus we are going to add. if (ier==FITPACK_OK) then npl1 = nplus*2 rn = nplus if (fpold-fp>acc) npl1 = int(rn*fpms/(fpold-fp)) nplus = min(nplus*2,max(npl1,nplus/2,1)) else nplus = 1 ier = FITPACK_OK end if ! Initialize iterate fpold = fp ! compute the sum of squared residuals for each knot interval ! t(j+k) <= u(i) <= t(j+k+1) and store it in fpint(j),j=1,2,...nrint. fpart = zero i = 1 l = k2 new = .false. jj = (mb-1)*idim square_residuals: do it=mb,me if (u(it)>=t(l) .and. l<=nk1) then new = .true. l = l+1 endif term = zero l0 = l-k2 do j2=1,idim fac = dot_product(c(l0+1:l0+k1),q(it,1:k1)) jj = jj+1 term = term+(w(it)*(fac-x(jj)))**2 l0 = l0+n end do fpart = fpart+term if (new) then store = term*half fpint(i) = fpart-store i = i+1 fpart = store new = .false. endif end do square_residuals fpint(nrint) = fpart add_new_knots: do l=1,nplus ! add a new knot. call fpknot(u,m,t,n,fpint,nrdata,nrint,nest,1) ! if n=nmax we locate the knots as for interpolation if (n==nmax) then i = k2 j = 3-jb+k/2 do jj=1,mm t(i) = u(j) i = i+1 j = j+1 end do ! Restart main loop iter = 0 cycle main_loop end if ! test whether we cannot further increase the number of knots. if (n==nest) exit add_new_knots end do add_new_knots ! restart the computations with the new set of knots. end do main_loop ! test whether the least-squares kth degree polynomial curve is a ! solution of our approximation problem. if (ier==FITPACK_LEASTSQUARES_OK) return ! ***** ! part 2: determination of the smoothing spline curve sp(u). ! ***** ! we have determined the number of knots and their position. ! we now compute the b-spline coefficients of the smoothing curve sp(u). the observation matrix a ! is extended by the rows of matrix b expressing that the kth derivative discontinuities of sp(u) ! at the interior knots t(k+2),...t(n-k-1) must be zero. the corresponding weights of these ! additional rows are set to 1/p. ! iteratively we then have to determine the value of p such that f(p), the sum of squared residuals ! be = s. we already know that the least squares kth degree polynomial curve corresponds to p=0, ! and that the least-squares spline curve corresponds to p=infinity. the iteration process which is ! proposed here, makes use of rational interpolation. since f(p) is a convex and strictly ! decreasing function of p, it can be approximated by a rational function r(p) = (u*p+v)/(p+w). ! three values of p(p1,p2,p3) with corresponding values of f(p) (f1=f(p1)-s,f2=f(p2)-s,f3=f(p3)-s) ! are used to calculate the new value of p such that r(p)=s. convergence is guaranteed by taking ! f1>0 and f3<zero ! ***** ! evaluate the discontinuity jump of the kth derivative of the b-splines at the knots ! t(l),l=k+2,...n-k-1 and store in b. call fpdisc(t,n,k2,b,nest) ! initial value for p. p1 = zero f1 = fp0-s p3 = -one f3 = fpms p = sum(a(1:nn,1)) rn = nn p = rn/p check1 = .false. check3 = .false. n8 = n-nmin ! iteration process to find the root of f(p) = s. find_root: do iter=1,maxit ! the rows of matrix b with weight 1/p are rotated into the ! triangularised observation matrix a which is stored in g. pinv = one/p c = z g(1:nn,1:k1) = a(1:nn,1:k1) g(1:nn, k2) = zero b_rows: do it=1,n8 ! the row of matrix b is rotated into triangle by givens transformation h(:k2) = b(it,:k2)*pinv xi = zero ! take into account that certain b-spline coefficients must be zero. if (it<=ib) then j1 = ib-it+2 j2 = k2-j1+1 h(1:j2) = h(j1:k2) h(j2+1:k2) = zero endif jj = max(1,it-ib) b_cols: do j=jj,nn piv = h(1) ! calculate the parameters of the givens transformation. call fpgivs(piv,g(j,1),cos,sin) ! transformations to right hand side. call fprota(cos,sin,xi(1:idim),c(j:j+(idim-1)*n:n)) ! transformations to left hand side. if (j<nn) then i2 = min(nn-j,k1)+1 call fprota(cos,sin,h(2:i2),g(j,2:i2)) h(1:i2) = [h(2:i2),zero] endif end do b_cols end do b_rows ! backward substitution to obtain the b-spline coefficients. j1 = 1 do j2=1,idim j3 = j1+ib c(j3:j3+nn-1) = fpback(g,c(j1),nn,k2,nest) if (ib>0) c(j1:j1+ib-1) = zero j1 =j1+n end do ! computation of f(p). fp = zero l = k2 jj = (mb-1)*idim get_fp: do it=mb,me if (u(it)>=t(l) .and. l<=nk1) l = l+1 l0 = l-k2 term = zero do j2=1,idim fac = zero j1 = l0 do j=1,k1 j1 = j1+1 fac = fac+c(j1)*q(it,j) end do jj = jj+1 term = term+(fac-x(jj))**2 l0 = l0+n end do fp = fp+term*w(it)**2 end do get_fp ! SUCCESS! the approximation sp(u) is an acceptable solution. fpms = fp-s; if (abs(fpms)<acc) return ! find the new value of p and carry out one more step. call root_finding_iterate(p1,f1,p2,f2,p3,f3,p,fpms,acc,check1,check3,success) if (.not.success) then ier = FITPACK_S_TOO_SMALL return end if end do find_root ! Maximum number of iterations reached ier = FITPACK_MAXIT return end subroutine fpcons pure subroutine fpcosp(m,x,y,w,n,t,e,maxtr,maxbin,c,sq,sx,bind,nm,mb,a, & b,const,z,zz,u,q,info,up,left,right,jbind,ibind,ier) ! .. ! ..scalar arguments.. real(RKIND), intent(out) :: sq integer, intent(in) :: m,n,maxtr,maxbin,nm,mb integer, intent(out) :: ier ! ..array arguments.. real(RKIND), intent(in) :: x(m),y(m),w(m),t(n),e(n) real(RKIND), intent(inout) :: c(n),sx(m),a(n,4),b(nm,maxbin),const(n),z(n),zz(n),u(maxbin),q(m,4) integer, intent(inout) :: info(maxtr),up(maxtr),left(maxtr),right(maxtr) integer, intent(inout) :: ibind(mb),jbind(mb) logical, intent(inout) :: bind(n) ! ..local scalars.. integer :: count,i,i1,j,j1,j2,j3,k,kdim,k1,k2,k3,k4,k5,k6,l,l1,l2,l3,merk,nbind,violated,n1,n4,n6 real(RKIND) :: f,wi,xi ! ..local array.. real(RKIND) :: h(MAX_ORDER+1) ! ..subroutine references.. ! fpbspl,fpadno,fpdeno,fpfrno,fpseno ! .. ! ***** ! if we use the b-spline representation of s(x) our approximation problem results in a quadratic ! programming problem: find the b-spline coefficients c(j),j=1,2,...n-4 such that ! (1) sumi((wi*(yi-sumj(cj*nj(xi))))**2),i=1,2,...m is minimal ! (2) sumj(cj*n''j(t(l+3)))*e(l) <= 0, l=1,2,...n-6. ! to solve this problem we use the theil-van de panne procedure. ! if the inequality constraints (2) are numbered from 1 to n-6, this algorithm finds a subset of ! constraints ibind(1)..ibind(nbind) such that the solution of the minimization problem (1) with ! these constraints in equality form, satisfies all constraints. such a feasible solution is optimal ! if the lagrange parameters associated with that problem with equality constraints are all positive. ! ***** ! determine n6, the number of inequality constraints. n6 = n-6 n4 = n-4 ! fix the parameters which determine these constraints. forall (i=1:n6) const(i) = e(i)*(t(i+4)-t(i+1))/(t(i+5)-t(i+2)) ! initialize the triply linked tree which is used to find the subset of constraints ! ibind(1),...ibind(nbind). count = 1 info(1) = 0 left(1) = 0 right(1) = 0 up(1) = 1 merk = 1 ! set up the normal equations n'nc=n'y where n denotes the m x (n-4) observation matrix with ! elements ni,j = wi*nj(xi) and y is the column vector with elements yi*wi. ! from the properties of the b-splines nj(x),j=1,2,...n-4, it follows that n'n is a (n-4) x (n-4) ! positive definite bandmatrix of bandwidth 7. the matrices n'n and n'y are built up in a and z. ! initialization z(1:n4) = zero a(1:n4,1:4) = zero l = 4 rows: do i=1,m ! fetch the current row of the observation matrix. xi = x(i) wi = w(i)**2 ! search for knot interval t(l) <= xi < t(l+1) do while (xi>=t(l+1) .and. l/=n4) l = l+1 end do ! evaluate the four non-zero cubic b-splines nj(xi),j=l-3,...l. h = fpbspl(t,n,3,xi,l) ! store in q these values h(1),h(2),...h(4). q(i,1:4) = h(1:4) ! add the contribution of the current row of the observation matrix n to the normal equations. l3 = l-3 k1 = 0 add_row: do j1 = l3,l k1 = k1+1 f = h(k1) z(j1) = z(j1)+f*wi*y(i) k2 = k1 j2 = 4 do j3 = j1,l a(j3,j2) = a(j3,j2)+f*wi*h(k2) k2 = k2+1 j2 = j2-1 end do end do add_row end do rows ! since n'n is a symmetric matrix it can be factorized as ! (3) n'n = (r1)'(d1)(r1) ! with d1 a diagonal matrix and r1 an (n-4) x (n-4) unit upper triangular matrix of bandwidth 4. ! matrices r1 and d1 are built up in a. at the same time we solve the systems of equations ! (4) (r1)'(z2) = n'y ! (5) (d1) (z1) = (z2) ! the vectors z2 and z1 are kept in zz and z. solve: do i=1,n4 k1 = max(5-i,1) k2 = i-4+k1 k3 = k2 hundred: do j=k1,4 k4 = j-1 k5 = 4-j+k1 f = a(i,j) if (k1<=k4) then k6 = k2 do k=k1,k4 f = f-a(i,k)*a(k3,k5)*a(k6,4) k5 = k5+1 k6 = k6+1 end do end if if (j<4) then a(i,j) = f/a(k3,4) k3 = k3+1 endif end do hundred a(i,4) = f f = z(i) if(i>1) then k4 = i do j=k1,3 k = k1+3-j k4 = k4-1 f = f-a(i,k)*z(k4)*a(k4,4) end do endif z (i) = f/a(i,4) zz(i) = f end do solve ! start computing the least-squares cubic spline without taking account ! of any constraint. nbind = 0 n1 = 1 ibind(1) = 0 ! main loop for the least-squares problems with different subsets of the constraints (2) in equality ! form. the resulting b-spline coeff. c and lagrange parameters u are the solution of the system ! ! n'n b' ! ! c ! ! n'y ! ! (6) ! ! ! ! = ! ! ! ! b 0 ! ! u ! ! 0 ! ! z1 is stored into array c. least_squares: do c(1:n4) = z(1:n4) ! if there are no equality constraints, compute the coeff. c directly. has_constraints: if (nbind>0) then ! initialization kdim = n4+nbind b(1:kdim,1:nbind) = zero ! matrix b is built up,expressing that the constraints nrs ibind(1),... ! ibind(nbind) must be satisfied in equality form. do i=1,nbind l = ibind(i) b(l,i) = e(l) b(l+1,i) = -(e(l)+const(l)) b(l+2,i) = const(l) end do ! find the matrix (b1) as the solution of the system of equations ! (7) (r1)'(d1)(b1) = b' ! (b1) is built up in the upper part of the array b(rows 1,...n-4). make_b1: do k1=1,nbind l = ibind(k1) do i=l,n4 f = b(i,k1) if (i/=1) then k2 = min(i-1,3) do k3=1,k2 l1 = i-k3 l2 = 4-k3 f = f-b(l1,k1)*a(i,l2)*a(l1,4) end do endif b(i,k1) = f/a(i,4) end do end do make_b1 ! factorization of the symmetric matrix -(b1)'(d1)(b1) ! (8) -(b1)'(d1)(b1) = (r2)'(d2)(r2) ! with (d2) a diagonal matrix and (r2) an nbind x nbind unit upper triangular matrix. ! the matrices r2 and d2 are built up in the lower part of the array b (rows n-3,n-2, ! ...n-4+nbind). factor: do i=1,nbind i1 = i-1 do j=i,nbind f = zero do k=1,n4 f = f+b(k,i)*b(k,j)*a(k,4) end do k1 = n4+1 if (i1/=0) then do k=1,i1 f = f+b(k1,i)*b(k1,j)*b(k1,k) k1 = k1+1 end do endif b(k1,j) = merge(-f/b(k1,i),-f,i/=j) end do end do factor ! according to (3),(7) and (8) the system of equations (6) becomes ! ! (r1)' 0 ! ! (d1) 0 ! ! (r1) (b1) ! ! c ! ! n'y ! ! (9) ! ! ! ! ! ! ! ! = ! ! ! ! (b1)' (r2)'! ! 0 (d2) ! ! 0 (r2) ! ! u ! ! 0 ! ! backward substitution to obtain the b-spline coefficients c(j),j=1,..n-4 and the ! lagrange parameters u(j),j=1,2,...nbind. first step of the backward substitution: ! solve the system ! ! (r1)'(d1) 0 ! ! (c1) ! ! n'y ! ! (10) ! ! ! ! = ! ! ! ! (b1)'(d1) (r2)'(d2) ! ! (u1) ! ! 0 ! ! from (4) and (5) we know that this is equivalent to ! (11) (c1) = (z1) ! (12) (r2)'(d2)(u1) = -(b1)'(z2) solve2: do i=1,nbind f = dot_product(b(1:n4,i),zz(1:n4)) i1 = i-1 k1 = n4+1 if (i1/=0) then ! [FP] this is probably unnecessary do j=1,i1 f = f+u(j)*b(k1,i)*b(k1,j) k1 = k1+1 end do end if u(i) = -f/b(k1,i) end do solve2 ! second step of the backward substitution: solve the system ! ! (r1) (b1) ! ! c ! ! c1 ! ! (13) ! ! ! ! = ! ! ! ! 0 (r2) ! ! u ! ! u1 ! k1 = nbind k2 = kdim ! find the lagrange parameters u. finish2: do i=1,nbind f = u(k1) if (i/=1) then k3 = k1+1 do j=k3,nbind f = f-u(j)*b(k2,j) end do endif u(k1) = f k1 = k1-1 k2 = k2-1 end do finish2 ! find the b-spline coefficients c. c(1:n4) = c(1:n4) - matmul(b(1:n4,1:nbind),u(1:nbind)) end if has_constraints k1 = n4 do i=2,n4 k1 = k1-1 f = c(k1) k2 = max(5-i,1) k3 = k1 l = 3 do j=k2,3 k3 = k3+1 f = f-a(k3,l)*c(k3) l = l-1 end do c(k1) = f end do ! test whether the solution of the least-squares problem with the constraints ibind(1),... ! ibind(nbind) in equality form, satisfies all of the constraints (2). k = 1 ! number counts the number of violated inequality constraints. violated = 0 test_constraints: do j=1,n6 l = ibind(k) k = k+1 if (j==l) cycle test_constraints k = k-1 ! test whether constraint j is satisfied f = e(j)*(c(j)-c(j+1))+const(j)*(c(j+2)-c(j+1)) if(f<=zero) cycle test_constraints ! if constraint j is not satisfied, add a branch of length nbind+1 to the tree. the nodes ! of this branch contain in their information field the number of the constraints ibind(1), ! ...ibind(nbind) and j, arranged in increasing order. violated = violated+1 k1 = k-1 if (k1>0) jbind(1:k1) = ibind(1:k1) jbind(k) = j if (l>0) jbind(k+1:nbind+1) = ibind(k:nbind) call fpadno(maxtr,up,left,right,info,count,merk,jbind,n1,ier) ! test whether the storage space which is required for the tree, ! exceeds the available storage space. if (ier/=0) then ier = FITPACK_S_TOO_SMALL return end if end do test_constraints ! test whether the solution of the least-squares problem with equality constraints is a ! feasible solution. test_feasible: do if (violated/=0) then ! test whether there are still cases with nbind constraints in equality form to be considered. violated = 0 ! test whether there are still cases with nbind constraints in ! equality form to be considered. if (merk<=1) then nbind = n1 ! test whether the number of knots where s''(x)=0 exceeds maxbin. if(nbind>maxbin) then ier = 1 return end if n1 = n1+1 ibind(n1) = 0 ! search which cases with nbind constraints in equality form ! are going to be considered. call fpdeno(maxtr,up,left,right,nbind,merk) ! test whether the quadratic programming problem has a solution. if (merk==1) then ier = 3 return end if endif ! find a new case with nbind constraints in equality form. call fpseno(maxtr,up,left,right,info,merk,ibind,nbind) exit test_feasible ! cycle least_squares else ! test whether the feasible solution is optimal. ier = FITPACK_OK bind(1:n6) = .false. test_optimal: do i=1,nbind if (u(i)<=zero) then violated = 1 exit test_optimal endif j = ibind(i) bind(j) = .true. end do test_optimal ! SUCCESS! exit least_squares endif end do test_feasible end do least_squares ! evaluate s(x) at the data points x(i) and calculate the weighted ! sum of squared residual right hand sides sq. sq = zero l = 4 evaluate_error: do i=1,m do while (x(i)>=t(l+1) .and. l/=n4) l = l+1 end do sx(i) = c(l-3)*q(i,1)+c(l-2)*q(i,2)+c(l-1)*q(i,3)+c(l)*q(i,4) sq = sq+(w(i)*(y(i)-sx(i)))**2 end do evaluate_error return end subroutine fpcosp ! fpcsin calculates the integrals ress=integral((b-x)**3*sin(par*x)) ! and resc=integral((b-x)**3*cos(par*x)) over the interval (a,b), ! given sia=sin(par*a),coa=cos(par*a),sib=sin(par*b) and cob=cos(par*b) pure elemental subroutine fpcsin(a,b,par,sia,coa,sib,cob,ress,resc) ! ..scalar arguments.. real(RKIND), intent(in) :: a,b,par,sia,coa,sib,cob real(RKIND), intent(out) :: ress,resc ! ..local scalars.. integer :: i,j real(RKIND) :: ab,ab4,ai,alfa,beta,b2,b4,fac,f1,f2 real(RKIND), parameter :: eps = smallnum10 ab = b-a ab4 = ab**4 alfa = ab*par ! the way of calculating the integrals ress and resc depends on ! the value of alfa = (b-a)*par. if(abs(alfa)<=one) then ! ress and resc are found by evaluating a series expansion. fac = fourth f1 = fac f2 = zero i = 4 series: do j=1,5 i = i+1 ai = i fac = fac*alfa/ai f2 = f2+fac if (abs(fac)<=eps) exit series i = i+1 ai = i fac = -fac*alfa/ai f1 = f1+fac if (abs(fac)<=eps) exit series end do series ress = ab4*(coa*f2+sia*f1) resc = ab4*(coa*f1-sia*f2) else ! integration by parts. beta = one/alfa b2 = beta**2 b4 = six*b2**2 f1 = three*b2*(one-two*b2) f2 = beta*(one-six*b2) ress = ab4*(coa*f2+sia*f1+sib*b4) resc = ab4*(coa*f1-sia*f2+cob*b4) return endif end subroutine fpcsin pure subroutine fpcurf(iopt,x,y,w,m,xb,xe,k,s,nest,tol, & maxit,k1,k2,n,t,c,fp,fpint,z,a,b,g,q,nrdata,ier) ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: xb,xe,s,tol real(RKIND), intent(out) :: fp integer, intent(in) :: iopt,m,k,nest,maxit,k1,k2 integer, intent(out) :: n,ier ! ..array arguments.. real(RKIND), intent(in) :: x(m),y(m),w(m) real(RKIND), intent(inout) :: t(nest),c(nest),fpint(nest),z(nest),a(nest,k1),b(nest,k2),& g(nest,k2),q(m,k1) integer, intent(inout) :: nrdata(nest) ! ..local scalars.. real(RKIND) :: acc,cos,fpart,fpms,fpold,fp0,f1,f2,f3,p,pinv,piv,p1,p2,p3,rn,sin,store,& term,wi,xi,yi integer :: i,it,iter,i2,j,k3,l,l0,mk1,nk1,nmax,nmin,nplus,npl1,nrint,n8 ! ..local arrays.. real(RKIND) :: h(MAX_ORDER+1) logical :: new,check1,check3,success fpold = zero fp0 = zero nplus = 0 ! ***** ! part 1: determination of the number of knots and their position ! ***** ! given a set of knots we compute the least-squares spline sinf(x), and the corresponding sum ! of squared residuals fp=f(p=inf). ! if iopt=-1 sinf(x) is the requested approximation. ! if iopt=0 or iopt=1 we check whether we can accept the knots: ! if fp <=s we will continue with the current set of knots. ! if fp > s we will increase the number of knots and compute the corresponding least- ! squares spline until finally fp<=s. ! the initial choice of knots depends on the value of s and iopt. ! if s=0 we have spline interpolation; in that case the number of knots equals nmax = m+k+1. ! if (s>0 and iopt=0) we first compute the least-squares polynomial curve of degree k; ! n = nmin = 2*k+2 ! iopt=1 we start with the set of knots found at the last call of the routine, except for ! the case that s > fp0; then we compute directly the least-squares polynomial of degree k. ! ***** ! determine nmin, the number of knots for polynomial approximation. nmin = 2*k1 bootstrap: if (iopt>=0) then ! calculation of acc, the absolute tolerance for the root of f(p)=s. acc = tol*s ! determine nmax, the number of knots for spline interpolation. nmax = m+k1 interpolating: if (s<=zero) then ! if s=0, s(x) is an interpolating spline. ! test whether the required storage space exceeds the available one. n = nmax if (nmax>nest) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! find the position of the interior knots in case of interpolation. mk1 = m-k1 if (mk1/=0) then k3 = k/2 i = k2 j = k3+2 do l=1,mk1 t(i) = merge( x(j) , (x(j)+x(j-1))*half , k3*2/=k) i = i+1 j = j+1 end do endif else interpolating ! if s>0 our initial choice of knots depends on the value of iopt. ! if iopt=0 or iopt=1 and s>=fp0, we start computing the least-squares ! polynomial of degree k which is a spline without interior knots. ! if iopt=1 and fp0>s we start computing the least squares spline ! according to the set of knots found at the last call of the routine. use_last_call: if (iopt/=0 .and. n/=nmin) then fp0 = fpint(n) fpold = fpint(n-1) nplus = nrdata(n) if (fp0<=s) then n = nmin fpold = zero nplus = 0 nrdata(1) = m-2 endif else use_last_call n = nmin fpold = zero nplus = 0 nrdata(1) = m-2 endif use_last_call endif interpolating endif bootstrap ! main loop for the different sets of knots. m is a save upper bound ! for the number of trials. iter = 0 main_loop: do while (iter<=m) iter = iter+1 if (n==nmin) ier = FITPACK_LEASTSQUARES_OK ! find nrint, tne number of knot intervals. nrint = n-nmin+1 ! find the position of the additional knots which are needed for ! the b-spline representation of s(x). nk1 = n-k1 t(1:k1) = xb t(nk1+1:n) = xe ! compute the b-spline coefficients of the least-squares spline ! sinf(x). the observation matrix a is built up row by row and ! reduced to upper triangular form by givens transformations. ! at the same time fp=f(p=inf) is computed. fp = zero ! initialize the observation matrix a. z(1:nk1) = zero a(1:nk1,1:k1) = zero l = k1 coefs: do it=1,m ! fetch the current data point x(it),y(it). xi = x(it) wi = w(it) yi = y(it)*wi ! search for knot interval t(l) <= xi < t(l+1). do while (xi>=t(l+1) .and. l/=nk1) l = l+1 end do ! evaluate the (k+1) non-zero b-splines at xi and store them in q. h = fpbspl(t,n,k,xi,l) q(it,1:k1) = h(1:k1) h(:k1) = wi*h(:k1) ! rotate the new row of the observation matrix into triangle. j = l-k1 rotate_row: do i=1,k1 j = j+1 piv = h(i); if (equal(piv,zero)) cycle rotate_row ! calculate the parameters of the givens transformation. call fpgivs(piv,a(j,1),cos,sin) ! transformations to right hand side. call fprota(cos,sin,yi,z(j)) ! transformations to left hand side. if (i<k1) call fprota(cos,sin,h(i+1:k1),a(j,2:k1-i+1)) end do rotate_row ! add contribution of this row to the sum of squares of residual ! right hand sides. fp = fp+yi*yi end do coefs if (ier==FITPACK_LEASTSQUARES_OK) fp0 = fp fpint(n-1:n) = [fpold,fp0] nrdata(n) = nplus ! backward substitution to obtain the b-spline coefficients. c(:nk1) = fpback(a,z,nk1,k1,nest) ! test whether the approximation sinf(x) is an acceptable solution. if (iopt<0) return fpms = fp-s; if(abs(fpms)<acc) return ! if f(p=inf) < s accept the choice of knots. if (fpms<zero) exit main_loop ! if n = nmax, sinf(x) is an interpolating spline. if (n==nmax) then ier = FITPACK_INTERPOLATING_OK return end if ! increase the number of knots. ! if n=nest we cannot increase the number of knots because of the storage capacity limitation. if (n==nest) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! determine the number of knots nplus we are going to add. if (ier==FITPACK_OK) then npl1 = nplus*2 rn = nplus if (fpold-fp>acc) npl1 = int(rn*fpms/(fpold-fp)) nplus = min(nplus*2,max(npl1,nplus/2,1)) else nplus = 1 ier = FITPACK_OK endif ! Initialize update fpold = fp ! compute the sum((w(i)*(y(i)-s(x(i))))**2) for each knot interval ! t(j+k) <= x(i) <= t(j+k+1) and store it in fpint(j),j=1,2,...nrint. fpart = zero i = 1 l = k2 new = .false. square_residuals: do it=1,m if (x(it)>=t(l) .and. l<=nk1) then new = .true. l = l+1 endif l0 = l-k2 term = dot_product(c(l0+1:l0+k1),q(it,1:k1)) term = (w(it)*(term-y(it)))**2 fpart = fpart+term if (new) then store = term*half fpint(i) = fpart-store i = i+1 fpart = store new = .false. endif end do square_residuals fpint(nrint) = fpart add_new_knots: do l=1,nplus ! add a new knot. call fpknot(x,m,t,n,fpint,nrdata,nrint,nest,1) ! if n=nmax we locate the knots as for interpolation. if (n==nmax) then mk1 = m-k1 if (mk1/=0) then k3 = k/2 i = k2 j = k3+2 do l0=1,mk1 t(i) = merge( x(j) , (x(j)+x(j-1))*half , k3*2/=k) i = i+1 j = j+1 end do endif ! Restart main loop iter = 0 cycle main_loop end if ! test whether we cannot further increase the number of knots. if (n==nest) exit add_new_knots end do add_new_knots ! restart the computations with the new set of knots. end do main_loop ! test whether the least-squares kth degree polynomial is a solution ! of our approximation problem. if (ier==FITPACK_LEASTSQUARES_OK) return ! ***** ! part 2: determination of the smoothing spline sp(x). ! ***** ! we have determined the number of knots and their position. ! we now compute the b-spline coefficients of the smoothing spline sp(x). the observation matrix a ! is extended by the rows of matrix b expressing that the kth derivative discontinuities of sp(x) ! at the interior knots t(k+2),...t(n-k-1) must be zero. the corresponding weights of these ! additional rows are set to 1/p. ! iteratively we then have to determine the value of p such that f(p), the sum of squared ! residuals be = s. we already know that the least squares kth degree polynomial corresponds ! to p=0, and that the least-squares spline corresponds to p=infinity. the iteration process ! which is proposed here, makes use of rational interpolation. since f(p) is a convex and strictly ! decreasing function of p, it can be approximated by a rational function r(p) = (u*p+v)/(p+w). ! three values of p(p1,p2,p3) with corresponding values of f(p) (f1=f(p1)-s,f2=f(p2)-s,f3=f(p3)-s) ! are used to calculate the new value of p such that r(p)=s. convergence is guaranteed by taking ! f1>0 and f3<zero ! ***** ! evaluate the discontinuity jump of the kth derivative of the ! b-splines at the knots t(l),l=k+2,...n-k-1 and store in b. call fpdisc(t,n,k2,b,nest) ! initial value for p. p1 = zero f1 = fp0-s p3 = -one f3 = fpms p = sum(a(1:nk1,1)) rn = nk1 p = rn/p check1 = .false. check3 = .false. n8 = n-nmin ! iteration process to find the root of f(p) = s. iter = 0 find_root: do while (iter<maxit) iter = iter+1 ! the rows of matrix b with weight 1/p are rotated into the ! triangularised observation matrix a which is stored in g. pinv = one/p c(1:nk1) = z(1:nk1) g(1:nk1,1:k1) = a(1:nk1,1:k1) g(1:nk1, k2) = zero b_rows: do it=1,n8 ! the row of matrix b is rotated into triangle by givens transformation h(1:k2) = b(it,1:k2)*pinv yi = zero b_cols: do j=it,nk1 piv = h(1) ! calculate the parameters of the givens transformation. call fpgivs(piv,g(j,1),cos,sin) ! transformations to right hand side. call fprota(cos,sin,yi,c(j)) if (j==nk1) cycle b_rows ! transformations to left hand side. i2 = merge(nk1-j,k1,j>n8)+1 call fprota(cos,sin,h(2:i2),g(j,2:i2)) h(1:i2) = [h(2:i2),zero] end do b_cols end do b_rows ! backward substitution to obtain the b-spline coefficients. c(:nk1) = fpback(g,c,nk1,k2,nest) ! computation of f(p). fp = zero l = k2 get_fp: do it=1,m if (x(it)>=t(l) .and. l<=nk1) l = l+1 l0 = l-k2 term = dot_product(c(l0+1:l0+k1),q(it,:)) fp = fp+(w(it)*(term-y(it)))**2 end do get_fp ! SUCCESS! the approximation sp(x) is an acceptable solution. fpms = fp-s; if (abs(fpms)<acc) return ! find the new value of p and carry out one more step. call root_finding_iterate(p1,f1,p2,f2,p3,f3,p,fpms,acc,check1,check3,success) if (.not.success) then ier = FITPACK_S_TOO_SMALL return end if end do find_root ! Maximum number of iterations reached ier = FITPACK_MAXIT return end subroutine fpcurf ! subroutine fpcuro finds the real zeros of a cubic polynomial p(x) = a*x**3+b*x**2+c*x+d. pure subroutine fpcuro(a,b,c,d,x,n) ! ! calling sequence: ! call fpcuro(a,b,c,d,x,n) ! ! input parameters: ! a,b,c,d: real values, containing the coefficients of p(x). ! ! output parameters: ! x : real array,length 3, which contains the real zeros of p(x) ! n : integer, giving the number of real zeros of p(x). ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: a,b,c,d integer, intent(out) :: n ! ..array argument.. real(RKIND), intent(out) :: x(3) ! ..local scalars.. integer :: i real(RKIND) :: a1,b1,c1,df,disc,d1,f,p3,q,r,step,u,u1,u2,y ! set constants real(RKIND), parameter :: ovfl = 1.0e4_RKIND real(RKIND), parameter :: tent = 0.1_RKIND real(RKIND), parameter :: pi3 = datan(one)/0.75_RKIND real(RKIND), parameter :: e3 = tent/0.3_RKIND a1 = abs(a) b1 = abs(b) c1 = abs(c) d1 = abs(d) if (max(b1,c1,d1)<a1*ovfl) then ! p(x) is a third degree polynomial. b1 = b/a*e3 c1 = c/a d1 = d/a q = c1*e3-b1*b1 r = b1*b1*b1+(d1-b1*c1)*half disc = q*q*q+r*r if (disc>zero) then u = sqrt(disc) u1 = -r+u u2 = -r-u n = 1 x(1) = sign(abs(u1)**e3,u1)+sign(abs(u2)**e3,u2)-b1 else u = sign(sqrt(abs(q)),r) p3 = atan2(sqrt(-disc),abs(r))*e3 u2 = u+u n = 3 x(1) = -u2*cos(p3)-b1 x(2) = u2*cos(pi3-p3)-b1 x(3) = u2*cos(pi3+p3)-b1 end if elseif(max(c1,d1)<b1*ovfl) then ! p(x) is a second degree polynomial. disc = c*c-four*b*d if (disc<zero) then n = 0 return else n = 2 u = sqrt(disc) b1 = b+b x(1) = (-c+u)/b1 x(2) = (-c-u)/b1 endif elseif (d1<c1*ovfl) then ! p(x) is a first degree polynomial. n = 1 x(1) = -d/c else ! p(x) is a constant function. n = 0 return end if ! apply a newton iteration to improve the accuracy of the roots. do i=1,n y = x(i) f = ((a*y+b)*y+c)*y+d df = (three*a*y+two*b)*y+c step = merge(f/df,zero,abs(f)<abs(df)*tent) x(i) = y-step end do end subroutine fpcuro ! (l u)-decomposition of a cyclic tridiagonal matrix with the non-zero ! elements stored as follows ! ! | a(1,2) a(1,3) a(1,1) | ! | a(2,1) a(2,2) a(2,3) | ! | a(3,1) a(3,2) a(3,3) | ! | ............... | ! | a(n-1,1) a(n-1,2) a(n-1,3) | ! | a(n,3) a(n,1) a(n,2) | pure subroutine fpcyt1(a,n,nn) ! ..scalar arguments.. integer, intent(in) :: n,nn ! ..array arguments.. real(RKIND), intent(inout) :: a(nn,6) ! ..local scalars.. real(RKIND) aa,beta,gamma,sum,teta,v integer i,n1,n2 ! .. n2 = n-2 beta = one/a(1,2) gamma = a(n,3) teta = a(1,1)*beta a(1,4) = beta a(1,5) = gamma a(1,6) = teta sum = gamma*teta internal_rows: do i=2,n2 v = a(i-1,3)*beta aa = a(i,1) beta = one/(a(i,2)-aa*v) gamma = -gamma*v teta = -teta*aa*beta a(i,4) = beta a(i,5) = gamma a(i,6) = teta sum = sum+gamma*teta end do internal_rows n1 = n-1 v = a(n2,3)*beta aa = a(n1,1) beta = one/(a(n1,2)-aa*v) gamma = a(n,1)-gamma*v teta = (a(n1,3)-teta*aa)*beta a(n1,4) = beta a(n1,5) = gamma a(n1,6) = teta a(n,4) = one/(a(n,2)-(sum+gamma*teta)) return end subroutine fpcyt1 ! subroutine fpcyt2 solves a linear n x n system ! a * c = b ! where matrix a is a cyclic tridiagonal matrix, decomposed using subroutine fpsyt1. pure subroutine fpcyt2(a,n,b,c,nn) ! ..scalar arguments.. integer, intent(in) :: n,nn ! ..array arguments.. real(RKIND), intent(in) :: a(nn,6),b(n) real(RKIND), intent(out) :: c(n) ! ..local scalars.. real(RKIND) :: cc,sum integer :: i,j,j1,n1 ! .. c(1) = b(1)*a(1,4) sum = c(1)*a(1,5) n1 = n-1 do i=2,n1 c(i) = (b(i)-a(i,1)*c(i-1))*a(i,4) sum = sum+c(i)*a(i,5) end do cc = (b(n)-sum)*a(n,4) c(n) = cc c(n1) = c(n1)-cc*a(n1,6) j = n1 do i=3,n j1 = j-1 c(j1) = c(j1)-c(j)*a(j1,3)*a(j1,4)-cc*a(j1,6) j = j1 end do return end subroutine fpcyt2 ! subroutine fpdeno frees the nodes of all branches of a triply linked tree with length < nbind ! by putting to zero their up field. on exit the parameter merk points to the terminal node of ! the most left branch of length nbind or takes the value 1 if there is no such branch. pure subroutine fpdeno(maxtr,up,left,right,nbind,merk) ! .. ! ..scalar arguments.. integer, intent(in) :: maxtr,nbind integer, intent(out) :: merk ! ..array arguments.. integer, intent(inout) :: up(maxtr),left(maxtr),right(maxtr) ! ..local scalars .. integer :: i,j,k,l,level,point ! Begin from root node i = 1 level = 0 new_branch: do ! 10 ! Descend leftward until there are points move_left: do point = i i = left(point) if (i==0) exit move_left level = level+1 end do move_left ! Stopped at an intermediate level if (level/=nbind)then clear_node: do i = right(point) j = up(point) ! Free node up (point) = 0 k = left(j) if (point/=k) then descend_right: do l = right(k) if (point==l) exit descend_right k = l end do descend_right right(k) = i point = k i = right(point) exit clear_node elseif (i/=0) then ! Attach this node to the right left(j) = i cycle new_branch else level = level-1 if (level==0) exit new_branch point = j ! cycle clear_node endif end do clear_node endif ! level/=nbind ! Move up until we find a right branch; ! restart from that branch if found move_up: do i = right(point) if (i/=0) exit move_up ! Move up onw level i = up(point) level = level-1 if (level==0) exit new_branch ! Restart from upper level point = i end do move_up end do new_branch k = 1 l = left(k) merk = 1 if (up(l)==0) return find_merk: do while (k/=0) merk = k k = left(k) end do find_merk return end subroutine fpdeno ! subroutine fpdisc calculates the discontinuity jumps of the kth ! derivative of the b-splines of degree k at the knots t(k+2)..t(n-k-1) pure subroutine fpdisc(t,n,k2,b,nest) ! ..scalar arguments.. integer, intent(in) :: n,k2,nest ! ..array arguments.. real(RKIND), intent(in) :: t(n) real(RKIND), intent(inout) :: b(nest,k2) ! ..local scalars.. real(RKIND) :: an,fac,prod integer i,ik,j,jk,k,k1,l,lj,lk,lmk,lp,nk1,nrint ! ..local array.. real(RKIND) :: h(12) ! .. k1 = k2-1 k = k1-1 nk1 = n-k1 nrint = nk1-k an = nrint fac = an/(t(nk1+1)-t(k1)) do l=k2,nk1 lmk = l-k1 do j=1,k1 ik = j+k1 lj = l+j lk = lj-k2 h(j) = t(l)-t(lk) h(ik) = t(l)-t(lj) end do lp = lmk do j=1,k2 jk = j prod = h(j) do i=1,k jk = jk+1 prod = prod*h(jk)*fac end do lk = lp+k1 b(lmk,j) = (t(lk)-t(lp))/prod lp = lp+1 end do end do return end subroutine fpdisc ! subroutine fpfrno collects the free nodes (up field zero) of the triply linked tree the ! information of which is kept in the arrays up,left,right and info. the maximal length of the ! branches of the tree is given by n1. if no free nodes are found, the error flag ier is set ! to 1. pure subroutine fpfrno(maxtr,up,left,right,info,point,merk,n1,count,ier) ! ..scalar arguments.. integer, intent(in) :: maxtr,n1 integer, intent(inout) :: point,merk integer, intent(out) :: count,ier ! ..array arguments.. integer, intent(inout) :: up(maxtr),left(maxtr),right(maxtr),info(maxtr) ! ..local scalars integer :: i,j,k,l,n,level ! .. ier = 1 level = 1 count = 2 if (n1==2) return ! No free nodes free_nodes_left: do while (level<=n1) j = 0 i = 1 k = 0 l = 0 inner : do march_left: do while (j/=level) l = left(i) if (l==0) exit march_left i = l j = j+1 end do march_left do if (i<count .or. l==0) then exit elseif (i==count) then count = count+1 exit elseif (up(count)==0) then up (count) = up (i) left (count) = left (i) right(count) = right(i) info (count) = info (i) if( merk==i) merk = count if(point==i) point = count if (k==0) then n = up(i) left(n) = count else right(k) = count endif l = left(i) do while (l/=0) up(l) = count l = right(l) end do up(i) = 0 i = count count = count+1 exit else count = count+1 endif end do hundred10: do l = right(i) k = i if (l==0) then l = up(i) j = j-1 if (j==0) then exit inner else i = l ! cycle hundred10 endif else i = l exit hundred10 endif end do hundred10 end do inner level = level+1 end do free_nodes_left if(count<=maxtr) ier = FITPACK_OK end subroutine fpfrno ! subroutine fpgivs calculates the parameters of a givens transformation . elemental subroutine fpgivs(piv,ww,cos,sin) real(RKIND), intent(in) :: piv real(RKIND), intent(inout) :: ww real(RKIND), intent(out) :: cos,sin ! ..local scalars.. real(RKIND) :: dd,store store = abs(piv) dd = merge(store*sqrt(one+(ww/piv)**2), & ww *sqrt(one+(piv/ww)**2), store>=ww) cos = ww/dd sin = piv/dd ww = dd return end subroutine fpgivs ! Compute spline coefficients on a rectangular grid pure subroutine fpgrdi(ifsu,ifsv,ifbu,ifbv,iback,u,mu,v, & mv,z,mz,dz,iop0,iop1,tu,nu,tv,nv,p,c,nc,sq,fp,fpu,fpv,mm, & mvnu,spu,spv,right,q,au,av1,av2,bu,bv,aa,bb,cc,cosi,nru,nrv) ! .. ! ..scalar arguments.. real(RKIND), intent(out) :: sq real(RKIND), intent(inout) :: fp ! computed if iback==0 real(RKIND), intent(out) :: p integer , intent(inout) :: ifsu,ifsv,ifbu,ifbv integer , intent(in) :: iback,mu,mv,mz,iop0,iop1,nu,nv,nc,mm,mvnu ! ..array arguments.. real(RKIND), intent(inout) :: fpu(nu),fpv(nv) ! if iback==0 real(RKIND), intent(inout) :: bu(nu,5),bv(nv,5) ! ifbu,ifbv real(RKIND), intent(in) :: u(mu),v(mv),z(mz),dz(3),tu(nu),tv(nv) real(RKIND), intent(inout) :: spu(mu,4),spv(mv,4),cosi(2,nv),au(nu,5),av1(nv,6),av2(nv,4),right(mm), & aa(2,mv),bb(2,nv),c(nc),cc(nv),q(mvnu) integer, intent(inout) :: nru(mu),nrv(mv) ! ..local scalars.. real(RKIND) :: arg,co,dz1,dz2,dz3,fac,fac0,pinv,piv,si,term integer :: i,ic,ii,ij,ik,iq,irot,it,iz,i0,i1,i2,i3,j,jk,jper,j0,k,k1,& l,l0,l1,mvv,ncof,nrold,nroldu,nroldv,number,numu,numu1,numv,& numv1,nuu,nu4,nu7,nu8,nu9,nv11,nv4,nv7,nv8,n1 ! ..local arrays.. real(RKIND) :: h(MAX_ORDER+1),h1(5),h2(4) ! let ! | (spu) | | (spv) | ! (au) = | ---------- | (av) = | ---------- | ! | (1/p) (bu) | | (1/p) (bv) | ! ! | z ' 0 | ! q = | ------ | ! | 0 ' 0 | ! ! with c : the (nu-4) x (nv-4) matrix which contains the b-spline ! coefficients. ! z : the mu x mv matrix which contains the function values. ! spu,spv: the mu x (nu-4), resp. mv x (nv-4) observation matrices ! according to the least-squares problems in the u-,resp. ! v-direction. ! bu,bv : the (nu-7) x (nu-4),resp. (nv-7) x (nv-4) matrices ! containing the discontinuity jumps of the derivatives ! of the b-splines in the u-,resp.v-variable at the knots ! the b-spline coefficients of the smoothing spline are then calculated ! as the least-squares solution of the following over-determined linear ! system of equations ! ! (1) (av) c (au)' = q ! ! subject to the constraints ! ! (2) c(i,nv-3+j) = c(i,j), j=1,2,3 ; i=1,2,...,nu-4 ! ! (3) if iop0 = 0 c(1,j) = dz(1) ! iop0 = 1 c(1,j) = dz(1) ! c(2,j) = dz(1)+(dz(2)*cosi(1,j)+dz(3)*cosi(2,j))* ! tu(5)/3. = cc(j) , j=1,2,...nv-4 ! ! (4) if iop1 = 1 c(nu-4,j) = 0, j=1,2,...,nv-4. ! ! initialization nu4 = nu-4 nu7 = nu-7 nu8 = nu-8 nu9 = nu-9 nv4 = nv-4 nv7 = nv-7 nv8 = nv-8 nv11 = nv-11 nuu = nu4-iop0-iop1-1 pinv = merge(one/p,one,p>zero) ! it depends on the value of the flags ifsu,ifsv,ifbu,ifbv and iop0 and ! on the value of p whether the matrices (spu), (spv), (bu), (bv) and ! (cosi) still must be determined. if (ifsu==0) then ! calculate the non-zero elements of the matrix (spu) which is the ob- ! servation matrix according to the least-squares spline approximation ! problem in the u-direction. l = 4 l1 = 5 number = 0 do it=1,mu arg = u(it) do while (.not.(arg<tu(l1) .or. l==nu4)) l = l1 l1 = l+1 number = number+1 end do h = fpbspl(tu,nu,3,arg,l) spu(it,1:4) = h(1:4) nru(it) = number end do ifsu = 1 endif ! calculate the non-zero elements of the matrix (spv) which is the ob- ! servation matrix according to the least-squares spline approximation ! problem in the v-direction. if (ifsv==0) then l = 4 l1 = 5 number = 0 do it=1,mv arg = v(it) do while (.not.(arg<tv(l1) .or. l==nv4)) l = l1 l1 = l+1 number = number+1 end do h = fpbspl(tv,nv,3,arg,l) spv(it,1:4) = h(1:4) nrv(it) = number end do ifsv = 1 if (iop0/=0) then ! calculate the coefficients of the interpolating splines for cos(v) and sin(v). cosi(:,:nv4) = zero if (nv7>=4) then do i=1,nv7 l = i+3 arg = tv(l) h = fpbspl(tv,nv,3,arg,l) av1(i,1:3) = h(1:3) cosi(:,i) = [cos(arg),sin(arg)] end do call fpcyt1(av1,nv7,nv) do j=1,2 call fpcyt2(av1,nv7,cosi(j,1:nv7),right,nv) cosi(j,2:nv7+1) = right(1:nv7) cosi(j,1) = cosi(j,nv7+1) cosi(j,nv7+2) = cosi(j,2) cosi(j,nv4) = cosi(j,3) end do endif endif end if if (p>zero) then ! calculate the non-zero elements of the matrix (bu). if (ifbu==0 .and. nu8/=0) then call fpdisc(tu,nu,5,bu,nu) ifbu = 1 endif ! calculate the non-zero elements of the matrix (bv). if (ifbv==0 .and. nv8/=0) then call fpdisc(tv,nv,5,bv,nv) ifbv = 1 endif endif ! substituting (2),(3) and (4) into (1), we obtain the overdetermined system ! (5) (avv) (cr) (auu)' = (qq) ! from which the nuu*nv7 remaining coefficients ! c(i,j) , i=2+iop0,3+iop0,...,nu-4-iop1 ; j=1,2,...,nv-7 , ! the elements of (cr), are then determined in the least-squares sense. ! simultaneously, we compute the resulting sum of squared residuals sq. dz1 = dz(1) aa (1,1:mv) = dz1 if (nv8/=0 .and. p>zero) bb(1,1:nv8) = zero mvv = mv if (iop0/=0) then fac = tu(5)/three dz2 = dz(2)*fac dz3 = dz(3)*fac cc(1:nv4) = dz1+dz2*cosi(1,1:nv4)+dz3*cosi(2,1:nv4) forall (i=1:mv) aa(2,i) = dot_product(spv(i,1:4),cc(nrv(i)+1:nrv(i)+4)) if (nv8/=0 .and. p>zero) then forall(i=1:nv8) bb(2,i) = pinv*dot_product(cc(i:i+4),bv(i,1:5)) mvv = mvv+nv8 endif endif ! we first determine the matrices (auu) and (qq). then we reduce the ! matrix (auu) to upper triangular form (ru) using givens rotations. ! we apply the same transformations to the rows of matrix qq to obtain ! the (mv+nv8) x nuu matrix g. ! we store matrix (ru) into au and g into q. l = mvv*nuu ! initialization. sq = zero q(1:l) = zero au(1:nuu,1:5) = zero l = 0 nrold = 0 n1 = nrold+1 auu_iterations: do it=1,mu number = nru(it) ! find the appropriate column of q. inner: do right(1:mvv) = zero if (nrold/=number) then if (p<=zero) then nrold = n1 n1 = n1+1 cycle inner endif ! fetch a new row of matrix (bu). h(1:5) = bu(n1,1:5)*pinv i0 = 1 i1 = 5 else ! fetch a new row of matrix (spu). h(1:4) = spu(it,1:4) ! find the appropriate column of q. right(1:mv) = z(l+1:l+mv) l = l+mv i0 = 1 i1 = 4 endif if (nu7-number == iop1) i1 = i1-1 j0 = n1 ! take into account that we eliminate the constraints (3) do while (j0-1<=iop0) fac0 = h(i0) right(1:mv) = right(1:mv) -fac0*aa(j0,1:mv) if (mv/=mvv) right(mv+1:mv+nv8) = right(mv+1:mv+8)-fac0*bb(j0,1:nv8) j0 = j0+1 i0 = i0+1 end do irot = max(0,nrold-iop0-1) ! rotate the new row of matrix (auu) into triangle. rotate_auu: do i=i0,i1 irot = irot+1 piv = h(i) if (not_equal(piv,zero)) then ! calculate the parameters of the givens transformation. call fpgivs(piv,au(irot,1),co,si) ! apply that transformation to the rows of matrix (qq). iq = (irot-1)*mvv call fprota(co,si,right(1:mvv),q(iq+1:iq+mvv)) ! apply that transformation to the columns of (auu). if (i==i1) cycle rotate_auu i2 = 1 i3 = i+1 do j=i3,i1 i2 = i2+1 call fprota(co,si,h(j),au(irot,i2)) end do endif end do rotate_auu ! we update the sum of squared residuals sq = sq+sum(right(:mvv)**2) if (nrold==number) cycle auu_iterations nrold = n1 n1 = n1+1 end do inner end do auu_iterations ! we determine the matrix (avv) and then we reduce it to upper triangular form (rv) using ! givens rotations. we apply the same transformations to the columns of matrix g to obtain ! the (nv-7) x (nu-5-iop0-iop1) matrix h. we store matrix (rv) into av1 and av2, h into c. ! the nv7 x nv7 upper triangular matrix (rv) has the form ! | av1 ' | ! (rv) = | ' av2 | ! | 0 ' | ! with (av2) a nv7 x 4 matrix and (av1) a nv11 x nv11 upper triangular matrix of bandwidth 5. ncof = nuu*nv7 ! initialization. c (1:ncof) = zero av1(1:nv4,1:5) = zero av2(1:nv4,1:4) = zero jper = 0 nrold = 0 avv_iterations: do it=1,mv number = nrv(it) avv_inner: do if (nrold/=number) then if (p<=zero) then nrold = nrold+1 cycle avv_inner end if ! fetch a new row of matrix (bv). n1 = nrold+1 h(1:5) = bv(n1,1:5)*pinv ! find the appropriate row of g. if (mv/=mvv) then l = mv+n1 right(1:nuu) = q(l:l+(nuu-1)*mvv:mvv) else right(1:nuu) = zero endif else ! fetch a new row of matrix (spv) h(1:5) = [spv(it,1:4),zero] ! find the appropriate row of g. right(1:nuu) = q(it:it+(nuu-1)*mvv:mvv) endif ! test whether there are non-zero values in the new row of (avv) ! corresponding to the b-splines n(j,v),j=nv7+1,...,nv4. if (nrold>=nv11) then if (jper==0) then ! initialize the matrix (av2). jk = nv11+1 do i=1,4 ik = jk do j=1,5 if (ik<=0) exit av2(ik,i) = av1(ik,j) ik = ik-1 end do jk = jk+1 end do jper = 1 endif ! if one of the non-zero elements of the new row corresponds to one of the b-splines n(j;v), ! j=nv7+1,...,nv4, we take account of condition (2) for setting up this row of (avv). the row ! is stored in h1 the part with respect to av1) and h2 (the part with respect to av2). h1 = zero h2 = zero do i=1,5 j = nrold-nv11+i l0 = j l1 = l0-4 do while (l1>max(0,nv11)) l0 = l1-nv11 l1 = l0-4 end do if (l1<=0) then h2(l0) = h2(l0) + h(i) else ! (l1<=nv11) h1(l1) = h(i) endif end do ! rotate the new row of (avv) into triangle. if (nv11>0) then ! rotations with the rows 1,2,...,nv11 of (avv). avv_rot: do j=1,nv11 piv = h1(1) i2 = min(nv11-j,4) if (not_equal(piv,zero)) then ! calculate the parameters of the givens transformation. call fpgivs(piv,av1(j,1),co,si) ! apply that transformation to the columns of matrix g. ic = j do i=1,nuu call fprota(co,si,right(i),c(ic)) ic = ic+nv7 end do ! apply that transformation to the rows of (avv) with respect to av2. call fprota(co,si,h2(1:4),av2(j,1:4)) ! apply that transformation to the rows of (avv) with respect to av1. if(i2==0) exit avv_rot call fprota(co,si,h1(2:i2+1),av1(j,2:i2+1)) endif h1(1:i2+1) = [h1(2:i2+1),zero] end do avv_rot endif ! rotations with the rows nv11+1,...,nv7 of avv. avv_rot2: do j=1,4 ij = nv11+j piv = h2(j) if (ij>0 .and. not_equal(piv,zero)) then ! calculate the parameters of the givens transformation. call fpgivs(piv,av2(ij,j),co,si) ! apply that transformation to the columns of matrix g. ic = ij do i=1,nuu call fprota(co,si,right(i),c(ic)) ic = ic+nv7 end do ! apply that transformation to the rows of (avv) with respect to av2. if (j<4) call fprota(co,si,h2(j+1:4),av2(ij,j+1:4)) endif end do avv_rot2 else ! rotation into triangle of the new row of (avv), in case the elements ! corresponding to the b-splines n(j;v),j=nv7+1,...,nv4 are all zero. irot = nrold rot_avv_3: do i=1,5 irot = irot+1 piv = h(i) if (not_equal(piv,zero)) then ! calculate the parameters of the givens transformation. call fpgivs(piv,av1(irot,1),co,si) ! apply that transformation to the columns of matrix g. ic = irot do j=1,nuu call fprota(co,si,right(j),c(ic)) ic = ic+nv7 end do ! apply that transformation to the rows of (avv). if (i<5) then i2 = 1 i3 = i+1 do j=i3,5 i2 = i2+1 call fprota(co,si,h(j),av1(irot,i2)) end do endif endif end do rot_avv_3 endif ! we update the sum of squared residuals sq = sq+sum(right(:nuu)**2) if (nrold==number) exit avv_inner nrold = nrold+1 end do avv_inner end do avv_iterations ! test whether the b-spline coefficients must be determined. spline_coefs: if (iback==0) then ! backward substitution to obtain the b-spline coefficients as the solution of the linear ! system (rv) (cr) (ru)' = h. ! first step: solve the system (rv) (c1) = h. k = 1 do i=1,nuu c(k:k+nv7-1) = fpbacp(av1,av2,c(k),nv7,4,5,nv) k = k+nv7 end do ! second step: solve the system (cr) (ru)' = (c1). k = 0 do j=1,nv7 k = k+1 right(:nuu) = c(k:k+(nuu-1)*nv7:nv7) right(:nuu) = fpback(au,right,nuu,5,nu) c(k:k+(nuu-1)*nv7:nv7) = right(:nuu) end do ! calculate from the conditions (2)-(3)-(4), the remaining b-spline coefficients. ncof = nu4*nv4 j = 0 i = nv4 ! i = "last" element pointer q (1:nv4) = dz1 if (iop0/=0) then i=2*nv4 q(nv4+1:2*nv4) = cc(1:nv4) end if if (nuu/=0) then do l=1,nuu ii = i do k=1,nv7 i = i+1 j = j+1 q(i) = c(j) end do do k=1,3 ii = ii+1 i = i+1 q(i) = q(ii) end do end do endif if (iop1/=0) q(i+1:i+nv4) = zero c(1:ncof) = q(1:ncof) ! calculate the quantities ! res(i,j) = (z(i,j) - s(u(i),v(j)))**2 , i=1,2,..,mu; j=1,2,..,mv ! fp = sumi=1,mu(sumj=1,mv(res(i,j))) ! fpu(r) = sum''i(sumj=1,mv(res(i,j))) , r=1,2,...,nu-7, tu(r+3) <= u(i) <= tu(r+4) ! fpv(r) = sumi=1,mu(sum''j(res(i,j))) , r=1,2,...,nv-7, tv(r+3) <= v(j) <= tv(r+4) fp = zero fpu = zero fpv = zero iz = 0 nroldu = 0 ! main loop for the different grid points. do i1=1,mu numu = nru(i1) numu1 = numu+1 nroldv = 0 do i2=1,mv numv = nrv(i2) numv1 = numv+1 iz = iz+1 ! evaluate s(u,v) at the current grid point by making the sum of the ! cross products of the non-zero b-splines at (u,v), multiplied with ! the appropriate b-spline coefficients. term = zero k1 = numu*nv4+numv do l1=1,4 fac = spu(i1,l1) term = term + fac*dot_product(spv(i2,1:4),c(k1+1:k1+4)) k1 = k1+nv4 end do ! calculate the squared residual at the current grid point. term = (z(iz)-term)**2 ! adjust the different parameters. fp = fp+term fpu(numu1) = fpu(numu1)+term fpv(numv1) = fpv(numv1)+term fac = term*half if(numv/=nroldv) then fpv(numv1) = fpv(numv1)-fac fpv(numv) = fpv(numv)+fac endif nroldv = numv if (numu/=nroldu) then fpu(numu1) = fpu(numu1)-fac fpu(numu) = fpu(numu)+fac endif end do nroldu = numu end do endif spline_coefs end subroutine fpgrdi ! find the least-squares spline sinf(u,v) and calculate for each knot interval tu(j+3)<=u<=tu(j+4) ! (tv(j+3)<=v<=tv(j+4)) the sum of squared residuals fpintu(j),j=1,2,...,nu-7 (fpintv(j),j=1,2,... ! ,nv-7) for the data points having their absciss (ordinate)-value belonging to that interval. ! fp gives the total sum of squared residuals. pure subroutine fpgrpa(ifsu,ifsv,ifbu,ifbv,idim,ipar,u,mu,v,mv,z,mz,tu,nu,tv,nv,p,c,nc,fp,fpu,fpv, & mm,mvnu,spu,spv,right,q,au,au1,av,av1,bu,bv,nru,nrv) ! .. ! ..scalar arguments.. integer, intent(inout) :: ifsu ! (spu) needs to be determined if /=0 [todo: make logical] integer, intent(inout) :: ifsv ! (spv) needs to be determined integer, intent(inout) :: ifbu ! (bu) needs to be determined integer, intent(inout) :: ifbv ! (bv) needs to be determined integer, intent(in) :: idim,mu,mv,mz,nu,nv,nc,mm,mvnu ! sizes ! ..array arguments.. real(RKIND), intent(inout) :: c(nc*idim),right(mm*idim),q(mvnu),au(nu,5),av(nv,5),au1(nu,4),av1(nv,4) real(RKIND), intent(in) :: u(mu),v(mv),z(mz*idim),tu(nu),tv(nv) integer, intent(in) :: ipar(2) real(RKIND), intent(in) :: p real(RKIND), intent(out) :: fp,fpu(nu),fpv(nv) ! Only modified if ifsu, ifsv real(RKIND), intent(inout) :: spu(mu,4),spv(mv,4) integer, intent(inout) :: nru(mu),nrv(mv) ! Only modified if ifbu,ifbv real(RKIND), intent(inout) :: bu(nu,5),bv(nv,5) ! ..local scalars.. real(RKIND) :: arg,fac,term,value integer :: i,id,ii,it,iz,i1,i2,j,jz,k,k1,k2,l,l1,l2,mvv,k0,muu,ncof,nroldu,nroldv,number,nmd,numu, & numu1,numv,numv1,nuu,nvv,nu4,nu7,nu8,nv4,nv7,nv8,n33 ! ..local arrays.. real(RKIND) :: h(MAX_ORDER+1) ! ..subroutine references.. ! fpback,fpbspl,fpdisc,fpbacp,fptrnp,fptrpe ! .. ! let ! | (spu) | | (spv) | ! (au) = | ---------- | (av) = | ---------- | ! | (1/p) (bu) | | (1/p) (bv) | ! ! | z ' 0 | ! q = | ------ | ! | 0 ' 0 | ! ! with c : the (nu-4) x (nv-4) matrix which contains the b-spline coefficients. ! z : the mu x mv matrix which contains the function values. ! spu,spv: the mu x (nu-4), resp. mv x (nv-4) observation matrices according to the ! least-squares problems in the u-,resp. v-direction. ! bu,bv : the (nu-7) x (nu-4),resp. (nv-7) x (nv-4) matrices containing the discontinuity ! jumps of the derivatives of the b-splines in the u-,resp.v-variable at the knots ! ! the b-spline coefficients of the smoothing spline are then calculated as the least-squares ! solution of the following over-determined linear system of equations ! ! (1) (av) c (au)' = q ! ! subject to the constraints ! ! (2) c(nu-3+i,j) = c(i,j), i=1,2,3 ; j=1,2,...,nv-4 ! if(ipar(1)/=0) ! ! (3) c(i,nv-3+j) = c(i,j), j=1,2,3 ; i=1,2,...,nu-4 ! if(ipar(2)/=0) ! ! initialization nu4 = nu-4 nu7 = nu-7 nu8 = nu-8 nv4 = nv-4 nv7 = nv-7 nv8 = nv-8 muu = mu - merge(1,0,ipar(1)/=0) mvv = mv - merge(1,0,ipar(2)/=0) ! it depends on the value of the flags ifsu,ifsv,ifbu and ibvand ! on the value of p whether the matrices (spu), (spv), (bu) and (bv) ! still must be determined. compute_spu: if (ifsu==0) then ! calculate the non-zero elements of the matrix (spu) [mu x nu-4] which is the observation ! matrix according to the least-squares spline approximation problem in the u-direction. l = 4 l1 = 5 number = 0 spu_rows: do it=1,muu arg = u(it) do while (arg>=tu(l1) .and. l/=nu4) l = l1 l1 = l+1 number = number+1 end do h = fpbspl(tu,nu,3,arg,l) spu(it,1:4) = h(1:4) nru(it) = number end do spu_rows ! Set (spu) known ifsu = 1 endif compute_spu ! calculate the non-zero elements of the matrix (spv) [mv x nv-4] which is the observation ! matrix according to the least-squares spline approximation problem in the v-direction. compute_spv: if (ifsv==0) then l = 4 l1 = 5 number = 0 spv_rows: do it=1,mvv arg = v(it) do while (arg>=tv(l1) .and. l/=nv4) l = l1 l1 = l+1 number = number+1 end do h = fpbspl(tv,nv,3,arg,l) spv(it,1:4) = h(1:4) nrv(it) = number end do spv_rows ! Set (spv) known ifsv = 1 endif compute_spv if (p>zero) then ! calculate the non-zero elements of the matrix (bu). if (ifbu==0 .and. nu8/=0) then call fpdisc(tu,nu,5,bu,nu) ifbu = 1 endif ! calculate the non-zero elements of the matrix (bv). if (ifbv==0 .and. nv8/=0) then call fpdisc(tv,nv,5,bv,nv) ifbv = 1 endif endif ! substituting (2) and (3) into (1), we obtain the overdetermined system ! (4) (avv) (cr) (auu)' = (qq) ! from which the nuu*nvv remaining coefficients ! c(i,j) , i=1,...,nu-4-3*ipar(1) ; j=1,...,nv-4-3*ipar(2) , ! the elements of (cr), are then determined in the least-squares sense. ! we first determine the matrices (auu) and (qq). then we reduce the matrix (auu) to upper ! triangular form (ru) using givens rotations. ! we apply the same transformations to the rows of matrix qq to obtain the (mv) x nuu matrix g. ! we store matrix (ru) into au (and au1 if ipar(1)=1) and g into q. if (ipar(1)==0) then nuu = nu4 call fptrnp(mu,mv,idim,nu,nru,spu,p,bu,z,au,q,right) else nuu = nu7 call fptrpe(mu,mv,idim,nu,nru,spu,p,bu,z,au,au1,q,right) endif ! we determine the matrix (avv) and then we reduce this matrix to upper triangular form (rv) ! using givens rotations. we apply the same transformations to the columns of matrix g to obtain ! the (nvv) x (nuu) matrix h. we store matrix (rv) into av (and av1 if ipar(2)=1) and h into c. if (ipar(2)==0) then nvv = nv4 call fptrnp(mv,nuu,idim,nv,nrv,spv,p,bv,q,av,c,right) else nvv = nv7 call fptrpe(mv,nuu,idim,nv,nrv,spv,p,bv,q,av,av1,c,right) endif ! backward substitution to obtain the b-spline coefficients as the solution of the linear system ! (rv) (cr) (ru)' = h. ! first step: solve the system (rv) (c1) = h. ncof = nuu*nvv k = 1 do ii=1,idim do i=1,nuu if (ipar(2)/=0) then c(k:k+nvv-1) = fpbacp(av,av1,c(k),nvv,4,5,nv) else c(k:k+nvv-1) = fpback(av,c(k),nvv,5,nv) end if k = k+nvv end do end do ! second step: solve the system (cr) (ru)' = (c1). do ii=1,idim k = (ii-1)*ncof do j=1,nvv k = k+1 l = k do i=1,nuu right(i) = c(l) l = l+nvv end do if (ipar(1)/=0) then right(:nuu) = fpbacp(au,au1,right,nuu,4,5,nu) else right(:nuu) = fpback(au,right,nuu,5,nu) end if l = k do i=1,nuu c(l) = right(i) l = l+nvv end do end do end do ! calculate from the conditions (2)-(3), the remaining b-spline coefficients. if (ipar(2)/=0) then i = 0 j = 0 do id=1,idim do l=1,nuu ii = i do k=1,nvv i = i+1 j = j+1 q(i) = c(j) end do do k=1,3 ii = ii+1 i = i+1 q(i) = q(ii) end do end do end do ncof = nv4*nuu nmd = ncof*idim c(1:nmd) = q(1:nmd) endif if (ipar(1)/=0) then i = 0 j = 0 n33 = 3*nv4 do id=1,idim ii = i do k=1,ncof i = i+1 j = j+1 q(i) = c(j) end do do k=1,n33 ii = ii+1 i = i+1 q(i) = q(ii) end do end do ncof = nv4*nu4 nmd = ncof*idim c(1:nmd) = q(1:nmd) endif ! calculate the quantities ! res(i,j) = (z(i,j) - s(u(i),v(j)))**2 , i=1,2,..,mu;j=1,2,..,mv ! fp = sumi=1,mu(sumj=1,mv(res(i,j))) ! fpu(r) = sum''i(sumj=1,mv(res(i,j))) , r=1,2,...,nu-7 ! tu(r+3) <= u(i) <= tu(r+4) ! fpv(r) = sumi=1,mu(sum''j(res(i,j))) , r=1,2,...,nv-7 ! tv(r+3) <= v(j) <= tv(r+4) fp = zero fpu(1:nu) = zero fpv(1:nv) = zero nroldu = 0 ! main loop for the different grid points. u_points: do i1=1,muu numu = nru(i1) numu1 = numu+1 nroldv = 0 iz = (i1-1)*mv v_points: do i2=1,mvv numv = nrv(i2) numv1 = numv+1 iz = iz+1 ! evaluate s(u,v) at the current grid point by making the sum of the cross products of the ! non-zero b-splines at (u,v), multiplied with the appropriate b-spline coefficients. term = zero k0 = numu*nv4+numv jz = iz do id=1,idim k1 = k0 value = zero do l1=1,4 k2 = k1 fac = spu(i1,l1) do l2=1,4 k2 = k2+1 value = value+fac*spv(i2,l2)*c(k2) end do k1 = k1+nv4 end do ! calculate the squared residual at the current grid point. term = term+(z(jz)-value)**2 jz = jz+mz k0 = k0+ncof end do ! adjust the different parameters. fp = fp+term fpu(numu1) = fpu(numu1)+term fpv(numv1) = fpv(numv1)+term fac = term*half if (numv/=nroldv) then fpv(numv1) = fpv(numv1)-fac fpv(numv) = fpv(numv) +fac endif nroldv = numv if (numu/=nroldu) then fpu(numu1) = fpu(numu1)-fac fpu(numu) = fpu(numu) +fac endif end do v_points nroldu = numu end do u_points return end subroutine fpgrpa ! .. ! the b-spline coefficients of the smoothing spline are calculated as the least-squares ! solution of the over-determined linear system of equations (ay) c (ax)' = q where ! ! | (spx) | | (spy) | ! (ax) = | ---------- | (ay) = | ---------- | ! | (1/p) (bx) | | (1/p) (by) | ! ! | z ' 0 | ! q = | ------ | ! | 0 ' 0 | ! ! with c : the (ny-ky-1) x (nx-kx-1) matrix which contains the b-spline coefficients. ! z : the my x mx matrix which contains the function values. ! spx,spy: the mx x (nx-kx-1) and my x (ny-ky-1) observation matrices according to the ! least-squares problems in the x- and y-direction. ! bx,by : the (nx-2*kx-1) x (nx-kx-1) and (ny-2*ky-1) x (ny-ky-1) matrices which contain ! the discontinuity jumps of the derivatives of the b-splines in the x- and ! y-direction. pure subroutine fpgrre(ifsx,ifsy,ifbx,ifby,x,mx,y,my,z,mz, & kx,ky,tx,nx,ty,ny,p,c,nc,fp,fpx,fpy,mm,mynx,kx1,kx2,ky1,ky2, & spx,spy,right,q,ax,ay,bx,by,nrx,nry) ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: p real(RKIND), intent(inout) :: fp integer, intent(in) :: mx,my,mz,kx,ky,nx,ny,nc,mm,mynx,kx1,kx2,ky1,ky2 integer, intent(inout) :: ifsx,ifsy,ifbx,ifby ! ..array arguments.. real(RKIND), intent(in) :: x(mx),y(my),z(mz),tx(nx),ty(ny) real(RKIND), intent(inout) :: c(nc),spx(mx,kx1),spy(my,ky1),right(mm),q(mynx),ax(nx,kx2),bx(nx,kx2),& ay(ny,ky2),by(ny,ky2),fpx(nx),fpy(ny) integer , intent(inout) :: nrx(mx),nry(my) ! ..local scalars.. real(RKIND) :: arg,cos,fac,pinv,piv,sin,term integer :: i,ibandx,ibandy,ic,iq,irot,it,iz,i1,i2,i3,j,k,k1,l,l1,ncof,nk1x,nk1y,& nrold,nroldx,nroldy,number,numx,numx1,numy,numy1,n1 ! ..local arrays.. real(RKIND) :: h(MAX_ORDER+1) nk1x = nx-kx1 nk1y = ny-ky1 pinv = merge(one/p,one,p>zero) ! it depends on the value of the flags ifsx,ifsy,ifbx and ifby and on the value of p whether ! the matrices (spx),(spy),(bx) and (by) still must be determined. if (ifsx==0) then ! calculate the non-zero elements of the matrix (spx) which is the observation matrix ! according to the least-squares spline approximation problem in the x-direction. l = kx1 l1 = kx2 number = 0 get_nrx: do it=1,mx arg = x(it) do while (arg>=tx(l1) .and. l/=nk1x) l = l1 l1 = l+1 number = number+1 end do h = fpbspl(tx,nx,kx,arg,l) spx(it,1:kx1) = h(1:kx1) nrx(it) = number end do get_nrx ifsx = 1 endif if (ifsy==0) then ! calculate the non-zero elements of the matrix (spy) which is the observation matrix ! according to the least-squares spline approximation problem in the y-direction. l = ky1 l1 = ky2 number = 0 get_nry: do it=1,my arg = y(it) do while (arg>=ty(l1) .and. l/=nk1y) l = l1 l1 = l+1 number = number+1 end do h = fpbspl(ty,ny,ky,arg,l) spy(it,1:ky1) = h(1:ky1) nry(it) = number end do get_nry ifsy = 1 endif if (p>zero) then ! calculate the non-zero elements of the matrix (bx). if (ifbx==0 .and. nx/=2*kx1) then call fpdisc(tx,nx,kx2,bx,nx) ifbx = 1 endif ! calculate the non-zero el ements of the matrix (by). if (ifby==0 .and. ny/=2*ky1) then call fpdisc(ty,ny,ky2,by,ny) ifby = 1 endif endif ! reduce the matrix (ax) to upper triangular form (rx) using givens rotations. apply the ! same transformations to the rows of matrix q to obtain the my x (nx-kx-1) matrix g. ! store matrix (rx) into (ax) and g into q. l = my*nk1x ! initialization. q(1:l) = zero ax(1:nk1x,1:kx2) = zero l = 0 nrold = 0 ! ibandx denotes the bandwidth of the matrices (ax) and (rx). ibandx = kx1 givens_ax: do it=1,mx number = nrx(it) inner_ax: do if(nrold==number) then ! fetch a new row of matrix (spx). h(ibandx) = zero h(1:kx1) = spx(it,1:kx1) ! find the appropriate column of q. do j=1,my l = l+1 right(j) = z(l) end do irot = number elseif (p<=zero) then nrold = nrold+1 cycle inner_ax else ibandx = kx2 ! fetch a new row of matrix (bx). n1 = nrold+1 h(1:kx2) = bx(n1,1:kx2)*pinv ! find the appropriate column of q. right(1:my) = zero irot = nrold endif ! rotate the new row of matrix (ax) into triangle. rot_new_row: do i=1,ibandx irot = irot+1 piv = h(i) if (equal(piv,zero)) cycle rot_new_row ! calculate the parameters of the givens transformation. call fpgivs(piv,ax(irot,1),cos,sin) ! apply that transformation to the rows of matrix q. iq = (irot-1)*my call fprota(cos,sin,right(1:my),q(iq+1:iq+my)) ! apply that transformation to the columns of (ax). if (i<ibandx) then i2 = 1 i3 = i+1 do j=i3,ibandx i2 = i2+1 call fprota(cos,sin,h(j),ax(irot,i2)) end do endif end do rot_new_row if (nrold==number) exit inner_ax nrold = nrold+1 end do inner_ax end do givens_ax ! reduce the matrix (ay) to upper triangular form (ry) using givens rotations. apply the same ! transformations to the columns of matrix g to obtain the (ny-ky-1) x (nx-kx-1) matrix h. ! store matrix (ry) into (ay) and h into c. ncof = nk1x*nk1y ! initialization. c(1:ncof) = zero ay(1:nk1y,1:ky2) = zero nrold = 0 ! ibandy denotes the bandwidth of the matrices (ay) and (ry). ibandy = ky1 givens_ay: do it=1,my number = nry(it) inner_ay: do if (nrold==number) then ! fetch a new row of matrix (spy) h(ibandy) = zero h(1:ky1) = spy(it,1:ky1) ! find the appropriate row of g. l = it do j=1,nk1x right(j) = q(l) l = l+my end do irot = number elseif (p<=zero) then nrold = nrold+1 cycle inner_ay else ibandy = ky2 ! fetch a new row of matrix (by). n1 = nrold+1 h(1:ky2) = by(n1,1:ky2)*pinv ! find the appropriate row of g. right(1:nk1x) = zero irot = nrold endif ! rotate the new row of matrix (ay) into triangle. rot_new_rowy: do i=1,ibandy irot = irot+1 piv = h(i) if (equal(piv,zero)) cycle rot_new_rowy ! calculate the parameters of the givens transformation. call fpgivs(piv,ay(irot,1),cos,sin) ! apply that transformation to the columns of matrix g. ic = irot do j=1,nk1x call fprota(cos,sin,right(j),c(ic)) ic = ic+nk1y end do ! apply that transformation to the columns of matrix (ay). if (i<ibandy) then i2 = 1 i3 = i+1 do j=i3,ibandy i2 = i2+1 call fprota(cos,sin,h(j),ay(irot,i2)) end do endif end do rot_new_rowy if (nrold==number) exit inner_ay nrold = nrold+1 end do inner_ay end do givens_ay ! backward substitution to obtain the b-spline coefficients as the ! solution of the linear system (ry) c (rx)' = h. ! first step: solve the system (ry) (c1) = h. k = 1 do i=1,nk1x c(k:k+nk1y-1) = fpback(ay,c(k),nk1y,ibandy,ny) k = k+nk1y end do ! second step: solve the system c (rx)' = (c1). k = 0 do j=1,nk1y k = k+1 l = k do i=1,nk1x right(i) = c(l) l = l+nk1y end do right(:nk1x) = fpback(ax,right,nk1x,ibandx,nx) l = k do i=1,nk1x c(l) = right(i) l = l+nk1y end do end do ! calculate the quantities ! res(i,j) = (z(i,j) - s(x(i),y(j)))**2 , i=1,2,..,mx;j=1,2,..,my ! fp = sumi=1,mx(sumj=1,my(res(i,j))) ! fpx(r) = sum''i(sumj=1,my(res(i,j))) , r=1,2,...,nx-2*kx-1 ! tx(r+kx) <= x(i) <= tx(r+kx+1) ! fpy(r) = sumi=1,mx(sum''j(res(i,j))) , r=1,2,...,ny-2*ky-1 ! ty(r+ky) <= y(j) <= ty(r+ky+1) fp = zero fpx = zero fpy = zero nk1y = ny-ky1 iz = 0 nroldx = 0 ! main loop for the different grid points. grid_x: do i1=1,mx numx = nrx(i1) numx1 = numx+1 nroldy = 0 grid_y: do i2=1,my numy = nry(i2) numy1 = numy+1 iz = iz+1 ! evaluate s(x,y) at the current grid point by making the sum of the ! cross products of the non-zero b-splines at (x,y), multiplied with ! the appropriate b-spline coefficients. term = zero k1 = numx*nk1y+numy do l1=1,kx1 term = term+spx(i1,l1)*dot_product(spy(i2,1:ky1),c(k1+1:k1+ky1)) k1 = k1+nk1y end do ! calculate the squared residual at the current grid point. term = (z(iz)-term)**2 ! adjust the different parameters. fp = fp+term fpx(numx1) = fpx(numx1)+term fpy(numy1) = fpy(numy1)+term fac = term*half if (numy/=nroldy) then fpy(numy1) = fpy(numy1)-fac fpy(numy) = fpy(numy) +fac endif nroldy = numy if (numx/=nroldx) then fpx(numx1) = fpx(numx1)-fac fpx(numx) = fpx(numx) +fac endif end do grid_y nroldx = numx end do grid_x return end subroutine fpgrre pure subroutine fpgrsp(ifsu,ifsv,ifbu,ifbv,iback,u,mu,v, & mv,r,mr,dr,iop0,iop1,tu,nu,tv,nv,p,c,nc,sq,fp,fpu,fpv,mm, & mvnu,spu,spv,right,q,au,av1,av2,bu,bv,a0,a1,b0,b1,c0,c1, & cosi,nru,nrv) ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: p real(RKIND), intent(inout) :: fp,sq integer, intent(in) :: iback,mu,mv,mr,iop0,iop1,nu,nv,nc,mm,mvnu integer, intent(inout) :: ifsu,ifsv,ifbu,ifbv ! ..array arguments.. real(RKIND), intent(in) :: u(mu),v(mv),r(mr),dr(6),tu(nu),tv(nv) real(RKIND), intent(inout) :: av1(nv,6),av2(nv,4),cosi(2,nv),spu(mu,4),spv(mv,4),right(mm),bu(nu,5), & bv(nv,5),a0(2,mv),b0(2,nv),c0(nv),c1(nv),a1(2,mv),b1(2,nv),q(mvnu),& au(nu,5),c(nc),fpu(nu),fpv(nv) integer, intent(inout) :: nru(mu),nrv(mv) ! ..local scalars.. real(RKIND) :: arg,co,dr01,dr11,fac,fac0,fac1,pinv,piv,si,term integer :: i,ic,ii,ij,ik,iq,irot,it,ir,i0,i1,i2,i3,j,jj,jk,jper, & j0,j1,k,k1,l,l0,l1,mvv,ncof,nrold,nroldu,nroldv,number, & numu,numu1,numv,numv1,nuu,nu4,nu7,nu8,nu9,nv11,nv4,nv7,nv8,n1 ! ..local arrays.. real(RKIND) :: h(MAX_ORDER+1),h1(5),h2(4) ! let ! | (spu) | | (spv) | ! (au) = | -------------- | (av) = | -------------- | ! | sqrt(1/p) (bu) | | sqrt(1/p) (bv) | ! ! | r ' 0 | ! q = | ------ | ! | 0 ' 0 | ! ! with c : the (nu-4) x (nv-4) matrix which contains the b-spline ! coefficients. ! r : the mu x mv matrix which contains the function values. ! spu,spv: the mu x (nu-4), resp. mv x (nv-4) observation matrices ! according to the least-squares problems in the u-,resp. ! v-direction. ! bu,bv : the (nu-7) x (nu-4),resp. (nv-7) x (nv-4) matrices ! containing the discontinuity jumps of the derivatives ! of the b-splines in the u-,resp.v-variable at the knots ! the b-spline coefficients of the smoothing spline are then calculated ! as the least-squares solution of the following over-determined linear ! system of equations ! ! (1) (av) c (au)' = q ! ! subject to the constraints ! ! (2) c(i,nv-3+j) = c(i,j), j=1,2,3 ; i=1,2,...,nu-4 ! ! (3) if iop0 = 0 c(1,j) = dr(1) ! iop0 = 1 c(1,j) = dr(1) ! c(2,j) = dr(1)+(dr(2)*cosi(1,j)+dr(3)*cosi(2,j))* ! tu(5)/3. = c0(j) , j=1,2,...nv-4 ! ! (4) if iop1 = 0 c(nu-4,j) = dr(4) ! iop1 = 1 c(nu-4,j) = dr(4) ! c(nu-5,j) = dr(4)+(dr(5)*cosi(1,j)+dr(6)*cosi(2,j)) ! *(tu(nu-4)-tu(nu-3))/3. = c1(j) ! ! initialization nu4 = nu-4 nu7 = nu-7 nu8 = nu-8 nu9 = nu-9 nv4 = nv-4 nv7 = nv-7 nv8 = nv-8 nv11 = nv-11 nuu = nu4-iop0-iop1-2 pinv = merge(one/p,one,p>zero) ! it depends on the value of the flags ifsu,ifsv,ifbu,ifbv,iop0,iop1 ! and on the value of p whether the matrices (spu), (spv), (bu), (bv), ! (cosi) still must be determined. if (ifsu==0) then ! calculate the non-zero elements of the matrix (spu) which is the observation matrix ! according to the least-squares spline approximation problem in the u-direction. l = 4 l1 = 5 number = 0 do it=1,mu arg = u(it) do while (.not.(arg<tu(l1) .or. l==nu4)) l = l1 l1 = l+1 number = number+1 end do h = fpbspl(tu,nu,3,arg,l) spu(it,1:4) = h(1:4) nru(it) = number end do ifsu = 1 endif ! calculate the non-zero elements of the matrix (spv) which is the ob- ! servation matrix according to the least-squares spline approximation ! problem in the v-direction. if(ifsv==0) then l = 4 l1 = 5 number = 0 do it=1,mv arg = v(it) do while (.not.(arg<tv(l1) .or. l==nv4)) l = l1 l1 = l+1 number = number+1 end do h = fpbspl(tv,nv,3,arg,l) spv(it,1:4) = h(1:4) nrv(it) = number end do ifsv = 1 ! calculate the coefficients of the interpolating splines for cos(v) and sin(v). if (iop0/=0 .or. iop1/=0) then cosi(:,1:nv4) = zero if (nv7>=4) then do i=1,nv7 l = i+3 arg = tv(l) h = fpbspl(tv,nv,3,arg,l) av1(i,1:3) = h(1:3) cosi(:,i) = [cos(arg),sin(arg)] end do call fpcyt1(av1,nv7,nv) do j=1,2 call fpcyt2(av1,nv7,cosi(j,1:nv7),right,nv) cosi(j,1:nv4) = [right(nv7),right(1:nv7),right(1:2)] end do endif endif endif if (p>zero) then ! calculate the non-zero elements of the matrix (bu). if(ifbu==0 .and. nu8/=0) then call fpdisc(tu,nu,5,bu,nu) ifbu = 1 endif ! calculate the non-zero elements of the matrix (bv). if (ifbv==0 .and. nv8/=0) then call fpdisc(tv,nv,5,bv,nv) ifbv = 1 endif endif ! substituting (2),(3) and (4) into (1), we obtain the overdetermined system ! (5) (avv) (cc) (auu)' = (qq) ! from which the nuu*nv7 remaining coefficients ! c(i,j) , i=2+iop0,3+iop0,...,nu-5-iop1,j=1,2,...,nv-7. ! the elements of (cc), are then determined in the least-squares sense. ! simultaneously, we compute the resulting sum of squared residuals sq. dr01 = dr(1) dr11 = dr(4) a0(1,1:mv) = dr01 a1(1,1:mv) = dr11 if (nv8/=0 .and. p>zero) then b0(1,1:nv8) = zero b1(1,1:nv8) = zero endif mvv = mv if (iop0/=0) then fac = (tu(5)-tu(4))/three c0(1:nv4) = dr01+fac*matmul(dr(2:3),cosi(:,:nv4)) forall (i=1:mv) a0(2,i) = dot_product(spv(i,1:4),c0(nrv(i)+1:nrv(i)+4)) if (nv8/=0 .and. p>zero) then forall (i=1:nv8) b0(2,i) = pinv*dot_product(bv(i,1:5),c0(i:i+4)) mvv = mv+nv8 endif endif if (iop1/=0) then fac = (tu(nu4)-tu(nu4+1))/three c1(1:nv4) = dr11 + fac*matmul(dr(5:6),cosi(:,:nv4)) forall (i=1:mv) a1(2,i) = dot_product(c1(nrv(i)+1:nrv(i)+4),spv(i,1:4)) if (nv8/=0 .and. p>zero) then forall (i=1:nv8) b1(2,i) = pinv*dot_product(bv(i,1:5),c1(i:i+4)) mvv = mv+nv8 endif endif ! we first determine the matrices (auu) and (qq). then we reduce the matrix (auu) to an unit ! upper triangular form (ru) using givens rotations without square roots. we apply the same ! transformations to the rows of matrix qq to obtain the mv x nuu matrix g. ! we store matrix (ru) into au and g into q. l = mvv*nuu ! initialization. sq = zero if (l/=0) then q(1:l) = zero au(1:nuu,1:5) = zero l = 0 endif nrold = 0 n1 = nrold+1 auu_iterations: do it=1,mu number = nru(it) auu_inner: do ! find the appropriate column of q. right(1:mvv) = zero if (nrold==number) then ! fetch a new row of matrix (spu). h(1:4) = spu(it,1:4) ! find the appropriate column of q. right(1:mv) = r(l+1:l+mv) l = l+mv i0 = 1 i1 = 4 else if (p<=zero) then nrold = n1 n1 = n1+1 cycle auu_inner end if ! fetch a new row of matrix (bu). h(1:5) = bu(n1,1:5)*pinv i0 = 1 i1 = 5 end if j0 = n1 j1 = nu7-number ! take into account that we eliminate the constraints (3) do while (j0-1<=iop0) fac0 = h(i0) right(1:mv) = right(1:mv)-fac0*a0(j0,1:mv) if (mv/=mvv) then j = mv do jj=1,nv8 j = j+1 right(j) = right(j)-fac0*b0(j0,jj) end do endif j0 = j0+1 i0 = i0+1 end do ! take into account that we eliminate the constraints (4) do while (j1-1<=iop1) fac1 = h(i1) right(1:mv) = right(1:mv)-fac1*a1(j1,1:mv) if (mv/=mvv) then j = mv do jj=1,nv8 j = j+1 right(j) = right(j)-fac1*b1(j1,jj) end do endif j1 = j1+1 i1 = i1-1 end do irot = max(0,nrold-iop0-1) ! rotate the new row of matrix (auu) into triangle. if (i0<=i1) then auu_rot: do i=i0,i1 irot = irot+1 piv = h(i) if (equal(piv,zero)) cycle auu_rot ! calculate the parameters of the givens transformation. call fpgivs(piv,au(irot,1),co,si) ! apply that transformation to the rows of matrix (qq). iq = (irot-1)*mvv do j=1,mvv iq = iq+1 call fprota(co,si,right(j),q(iq)) end do ! apply that transformation to the columns of (auu). if (i<i1) then i2 = 1 i3 = i+1 do j=i3,i1 i2 = i2+1 call fprota(co,si,h(j),au(irot,i2)) end do endif end do auu_rot endif ! we update the sum of squared residuals. sq = sq+sum(right(:mvv)**2) if (nrold==number) exit auu_inner nrold = n1 n1 = n1+1 end do auu_inner end do auu_iterations if (nuu/=0) then ! we determine the matrix (avv) and then we reduce her to an unit upper triangular form (rv) ! using givens rotations without square roots. we apply the same transformations to the ! columns of matrix g to obtain the (nv-7) x (nu-6-iop0-iop1) matrix h. we store matrix (rv) ! into av1 and av2, h into c. the nv7 x nv7 triangular unit upper matrix (rv) has the form ! | av1 ' | ! (rv) = | ' av2 | ! | 0 ' | ! with (av2) a nv7 x 4 matrix and (av1) a nv11 x nv11 unit upper triangular matrix of ! bandwidth 5. ncof = nuu*nv7 ! initialization. c(:ncof) = zero av1(1:nv4,1:5) = zero av2(1:nv4,1:4) = zero jper = 0 nrold = 0 avv_iterations: do it=1,mv number = nrv(it) avv_inner: do if (nrold==number) then ! fetch a new row of matrix (spv) h(1:5) = [spv(it,1:4),zero] ! find the appropriate row of g. right(1:nuu) = q(it:it+(nuu-1)*mvv:mvv) else if (p<=zero) then nrold = nrold+1 cycle avv_inner endif ! fetch a new row of matrix (bv). n1 = nrold+1 h(1:5) = bv(n1,1:5)*pinv ! find the appropriate row of g. if(mv/=mvv) then l = mv+n1 right(1:nuu) = q(l:l+(nuu-1)*mvv:mvv) else right(1:nuu) = zero endif endif ! test whether there are non-zero values in the new row of (avv) ! corresponding to the b-splines n(j;v),j=nv7+1,...,nv4. if (nrold<nv11) then ! rotation into triangle of the new row of (avv), in case the elements ! corresponding to the b-splines n(j;v),j=nv7+1,...,nv4 are all zero. irot = nrold do i=1,5 irot = irot+1 piv = h(i) if (equal(piv,zero)) cycle ! calculate the parameters of the givens transformation. call fpgivs(piv,av1(irot,1),co,si) ! apply that transformation to the columns of matrix g. ic = irot call fprota(co,si,right(1:nuu),c(ic:ic+(nuu-1)*nv7:nv7)) ! apply that transformation to the rows of (avv). if (i<5) then i2 = 1 i3 = i+1 do j=i3,5 i2 = i2+1 call fprota(co,si,h(j),av1(irot,i2)) end do endif end do else ! initialize the matrix (av2). if (jper==0) then jk = nv11+1 do i=1,4 ik = jk do j=1,5 if (ik<=0) exit av2(ik,i) = av1(ik,j) ik = ik-1 end do jk = jk+1 end do jper = 1 endif ! if one of the non-zero elements of the new row corresponds to one of the b-splines ! n(j;v),j=nv7+1,...,nv4, we take account of condition (2) for setting up this row of ! (avv). the row is stored in h1(the part with respect to av1) and h2 (the part with ! respect to av2). h1 = zero h2 = zero j = nrold-nv11 do i=1,5 j = j+1 l0 = j l1 = l0-4 do while (l1>max(0,nv11)) l0 = l1-nv11 l1 = l0-4 end do if (l1<=0) then h2(l0) = h2(l0) + h(i) else ! l1<=nv11 h1(l1) = h(i) end if end do ! rotate the new row of (avv) into triangle. if (nv11>0) then ! rotations with the rows 1,2,...,nv11 of (avv). do j=1,nv11 piv = h1(1) i2 = min(nv11-j,4) if (not_equal(piv,zero)) then ! calculate the parameters of the givens transformation. call fpgivs(piv,av1(j,1),co,si) ! apply that transformation to the columns of matrix g. call fprota(co,si,right(1:nuu),c(j:j+nv7*(nuu-1):nv7)) ! apply that transformation to the rows of (avv) with respect to av2. call fprota(co,si,h2(1:4),av2(j,1:4)) ! apply that transformation to the rows of (avv) with respect to av1. if (i2/=0) call fprota(co,si,h1(2:i2+1),av1(j,2:i2+1)) endif h1(1:i2+1) = [h1(2:i2+1),zero] end do endif ! rotations with the rows nv11+1,...,nv7 of avv. avv_rot: do j=1,4 ij = nv11+j j1 = j+1 piv = h2(j) if (ij<=0 .or. equal(piv,zero)) cycle avv_rot ! calculate the parameters of the givens transformation. call fpgivs(piv,av2(ij,j),co,si) ! apply that transformation to the columns of matrix g. call fprota(co,si,right(1:nuu),c(ij:ij+nv7*(nuu-1):nv7)) ! apply that transformation to the rows of (avv) with respect to av2. if (j<4) call fprota(co,si,h2(j1:4),av2(ij,j1:4)) end do avv_rot end if ! we update the sum of squared residuals. sq = sq+sum(right(:nuu)**2) if (nrold==number) exit avv_inner nrold = nrold+1 end do avv_inner end do avv_iterations ! test whether the b-spline coefficients must be determined. if (iback/=0) return ! backward substitution to obtain the b-spline coefficients as the ! solution of the linear system (rv) (cr) (ru)' = h. ! first step: solve the system (rv) (c1) = h. k = 1 do i=1,nuu c(k:k+nv7-1) = fpbacp(av1,av2,c(k),nv7,4,5,nv) k = k+nv7 end do ! second step: solve the system (cr) (ru)' = (c1). do k=1,nv7 right(:nuu) = c(k:k+(nuu-1)*nv7:nv7) right(:nuu) = fpback(au,right,nuu,5,nu) c(k:k+(nuu-1)*nv7:nv7) = right(:nuu) end do endif ! calculate from the conditions (2)-(3)-(4), the remaining b-spline ! coefficients. ncof = nu4*nv4 q(1:nv4) = dr01 q(ncof-nv4+1:ncof) = dr11 i = nv4 j = 0 if (iop0/=0) then q(i+1:i+nv4) = c0(1:nv4) i = i+nv4 endif if(nuu/=0) then do l=1,nuu ii = i do k=1,nv7 i = i+1 j = j+1 q(i) = c(j) end do do k=1,3 ii = ii+1 i = i+1 q(i) = q(ii) end do end do endif if(iop1/=0) then do l=1,nv4 i = i+1 q(i) = c1(l) end do endif c(1:ncof) = q(1:ncof) ! calculate the quantities ! res(i,j) = (r(i,j) - s(u(i),v(j)))**2 , i=1,2,..,mu;j=1,2,..,mv ! fp = sumi=1,mu(sumj=1,mv(res(i,j))) ! fpu(r) = sum''i(sumj=1,mv(res(i,j))) , r=1,2,...,nu-7 ! tu(r+3) <= u(i) <= tu(r+4) ! fpv(r) = sumi=1,mu(sum''j(res(i,j))) , r=1,2,...,nv-7 ! tv(r+3) <= v(j) <= tv(r+4) fp = zero fpu = zero fpv = zero ir = 0 nroldu = 0 ! main loop for the different grid points. grid_u: do i1=1,mu numu = nru(i1) numu1 = numu+1 nroldv = 0 grid_v: do i2=1,mv numv = nrv(i2) numv1 = numv+1 ir = ir+1 ! evaluate s(u,v) at the current grid point by making the sum of the ! cross products of the non-zero b-splines at (u,v), multiplied with ! the appropriate b-spline coefficients. term = zero k1 = numu*nv4+numv do l1 = 1,4 fac = spu(i1,l1) term = term+fac*dot_product(spv(i2,1:4),c(k1+1:k1+4)) k1 = k1+nv4 end do ! calculate the squared residual at the current grid point. term = (r(ir)-term)**2 ! adjust the different parameters. fp = fp+term fpu(numu1) = fpu(numu1)+term fpv(numv1) = fpv(numv1)+term fac = term*half if (numv/=nroldv) then fpv(numv1) = fpv(numv1)-fac fpv(numv) = fpv(numv)+fac endif nroldv = numv if (numu/=nroldu) then fpu(numu1) = fpu(numu1)-fac fpu(numu) = fpu(numu)+fac endif end do grid_v nroldu = numu end do grid_u return end subroutine fpgrsp ! given the b-spline representation (knots t(j),j=1,2,...,n, b-spline coefficients c(j),j=1,2,..., ! n-k-1) of a spline of degree k, fpinst calculates the b-spline representation (knots ! tt(j),j=1,2,...,nn, b-spline coefficients cc(j),j=1,2,...,nn-k-1) of the same spline if an ! additional knot is inserted at the point x situated in the inter val t(l)<=x<t(l+1). ! iopt/=0: periodic spline; at leas one of the following conditions must be fulfilled: l>2*k or l<n-2*k. ! iopt==0: non-periodic spline pure subroutine fpinst(iopt,t,n,c,k,x,l,tt,nn,cc,nest) ! ! ..scalar arguments.. integer, intent(in) :: k,n,l,iopt,nest integer, intent(out) :: nn real(RKIND), intent(in) :: x ! ..array arguments.. real(RKIND), intent(in) :: t(nest),c(nest) real(RKIND), intent(out) :: tt(nest),cc(nest) ! ..local scalars.. real(RKIND) :: fac,per integer :: i,i1,j,k1,m,mk,nk,nk1,nl,ll ! .. k1 = k+1 nk1 = n-k1 ! the new knots ll = l+1 tt(1:n+1) = [t(1:l),x,t(ll:n)] ! the new b-spline coefficients i = nk1 do j=l,nk1 cc(i+1) = c(i) i = i-1 end do i = l do j=1,k m = i+k1 fac = (x-tt(i))/(tt(m)-tt(i)) i1 = i-1 cc(i) = fac*c(i)+(one-fac)*c(i1) i = i1 end do cc(1:i) = c(1:i) nn = n+1 ! incorporate the boundary conditions for a periodic spline. if (iopt/=0) then nk = nn-k nl = nk-k1 per = tt(nk)-tt(k1) i = k1 j = nk if (ll>nl) then do m=1,k mk = m+nl cc(m) = cc(mk) i = i-1 j = j-1 tt(i) = tt(j)-per end do elseif (ll<=(k1+k)) then do m=1,k mk = m+nl cc(mk) = cc(m) i = i+1 j = j+1 tt(j) = tt(i)+per end do endif endif end subroutine fpinst pure subroutine fpintb(t,n,bint,nk1,x,y) ! subroutine fpintb calculates integrals of the normalized b-splines ! nj,k+1(x) of degree k, defined on the set of knots t(j),j=1,2,...n. ! it makes use of the formulae of gaffney for the calculation of ! indefinite integrals of b-splines. ! ! calling sequence: ! call fpintb(t,n,bint,nk1,x,y) ! ! input parameters: ! t : real array,length n, containing the position of the knots. ! n : integer value, giving the number of knots. ! nk1 : integer value, giving the number of b-splines of degree k, ! defined on the set of knots ,i.e. nk1 = n-k-1. ! x,y : real values, containing the end points of the integration ! interval. ! output parameter: ! bint : array,length nk1, containing the integrals of the b-splines. ! .. ! ..scalars arguments.. integer, intent(in) :: n,nk1 real(RKIND), intent(in) :: x,y ! ..array arguments.. real(RKIND), intent(in) :: t(n) real(RKIND), intent(out) :: bint(nk1) ! ..local scalars.. integer :: i,ia,ib,it,j,j1,k,k1,l,li,lj,lk,l0 logical :: lmin real(RKIND) :: a,ak,arg,b,f ! ..local arrays.. real(RKIND) aint(6),h(MAX_ORDER+1),h1(6) integer, parameter :: nit = 2 ! number of iterations ! initialization. k1 = n-nk1 ak = k1 k = k1-1 bint = zero ! the integration limits are arranged in increasing order. if (equal(x,y)) return a = min(x,y) b = max(x,y) lmin = x>y a = max(t(k1),a) b = min(t(nk1+1),b) if (a>b) return ! using the expression of gaffney for the indefinite integral of a b-spline we find that ! bint(j) = (t(j+k+1)-t(j))*(res(j,b)-res(j,a))/(k+1) ! where for t(l) <= x < t(l+1) ! res(j,x) = 0, j=1,2,...,l-k-1 ! = 1, j=l+1,l+2,...,nk1 ! = aint(j+k-l+1), j=l-k,l-k+1,...,l ! = sumi((x-t(j+i))*nj+i,k+1-i(x)/(t(j+k+1)-t(j+i))) ! i=0,1,...,k l = k1 l0 = l+1 ! set arg = a. arg = a ia = 0 iterations: do it=1,nit ! search for the knot interval t(l) <= arg < t(l+1). do while (.not.(arg<t(l0) .or. l==nk1)) l = l0 l0 = l+1 end do ! calculation of aint(j), j=1,2,...,k+1. ! initialization. aint(1) = (arg-t(l))/(t(l+1)-t(l)) aint(2:) = zero h1(1) = one do j=1,k ! evaluation of the non-zero b-splines of degree j at arg,i.e. ! h(i+1) = nl-j+i,j(arg), i=0,1,...,j. h(1) = zero do i=1,j li = l+i lj = li-j f = h1(i)/(t(li)-t(lj)) h(i) = h(i)+f*(t(li)-arg) h(i+1) = f*(arg-t(lj)) end do ! updating of the integrals aint. j1 = j+1 do i=1,j1 li = l+i lj = li-j1 aint(i) = aint(i)+h(i)*(arg-t(lj))/(t(li)-t(lj)) h1(i) = h(i) end do end do if (it<nit) then ! updating of the integrals bint lk = l-k ia = lk do i=1,k1 bint(lk) = -aint(i) lk = lk+1 end do arg = b endif end do iterations ! updating of the integrals bint. lk = l-k ib = lk-1 do i=1,k1 bint(lk) = bint(lk)+aint(i) lk = lk+1 end do if (ib>=ia) bint(ia:ib) = bint(ia:ib)+one ! the scaling factors are taken into account. f = one/ak do i=1,nk1 j = i+k1 bint(i) = bint(i)*(t(j)-t(i))*f end do ! the order of the integration limits is taken into account. if (lmin) bint = -bint end subroutine fpintb ! subroutine fpknot locates an additional knot for a spline of degree k and adjusts the ! corresponding parameters,i.e. ! t : the position of the knots. ! n : the number of knots. ! nrint : the number of knotintervals. ! fpint : the sum of squares of residual right hand sides ! for each knot interval. ! nrdata: the number of data points inside each knot interval. ! istart indicates that the smallest data point at which the new knot may be added is x(istart+1) pure subroutine fpknot(x,m,t,n,fpint,nrdata,nrint,nest,istart) ! .. ! ..scalar arguments.. integer, intent(in) :: m,nest,istart integer, intent(inout) :: n,nrint ! ..array arguments.. real(RKIND), intent(in) :: x(m) real(RKIND), intent(inout) :: t(nest) real(RKIND), intent(inout) :: fpint(nest) integer , intent(inout) :: nrdata(nest) ! ..local scalars.. real(RKIND) :: an,am,fpmax integer :: ihalf,j,jbegin,jj,jk,jpoint,k,maxbeg,maxpt,next,nrx,number ! .. number = 0 maxpt = 0 maxbeg = 0 k = (n-nrint-1)/2 ! search for knot interval t(number+k) <= x <= t(number+k+1) where fpint(number) is maximal on the ! condition that nrdata(number)/=0 . fpmax = zero jbegin = istart do j=1,nrint jpoint = nrdata(j) if (fpmax<fpint(j) .and. jpoint/=0) then fpmax = fpint(j) number = j maxpt = jpoint maxbeg = jbegin endif jbegin = jbegin+jpoint+1 end do ! let coincide the new knot t(number+k+1) with a data point x(nrx) ! inside the old knot interval t(number+k) <= x <= t(number+k+1). ihalf = maxpt/2+1 nrx = maxbeg+ihalf next = number+1 ! adjust the different parameters. if(next<=nrint) then do j=next,nrint jj = next+nrint-j fpint(jj+1) = fpint(jj) nrdata(jj+1) = nrdata(jj) jk = jj+k t(jk+1) = t(jk) end do endif nrdata(number) = ihalf-1 nrdata(next) = maxpt-ihalf am = maxpt an = nrdata(number) fpint(number) = fpmax*an/am an = nrdata(next) fpint(next) = fpmax*an/am jk = next+k t(jk) = x(nrx) n = n+1 nrint = nrint+1 return end subroutine fpknot ! given the set of function values z(i,j) defined on the rectangular grid (u(i),v(j)), ! i=1,2,...,mu;j=1,2,...,mv, fpopdi determines a smooth bicubic spline approximation with ! given knots tu(i),i=1,..,nu in the u-direction and tv(j),j=1,2,...,nv in the v-direction. ! this spline sp(u,v) will be periodic in the variable v and will satisfy the following ! constraints ! ! s(tu(1),v) = dz(1) , tv(4) <=v<= tv(nv-3) ! ! and (if iopt(2) = 1) ! ! d s(tu(1),v) ! ------------ = dz(2)*cos(v)+dz(3)*sin(v) , tv(4) <=v<= tv(nv-3) ! d u ! ! and (if iopt(3) = 1) ! ! s(tu(nu),v) = 0 tv(4) <=v<= tv(nv-3) ! ! where the parameters dz(i) correspond to the derivative values g(i,j) as defined in ! subroutine pogrid. ! ! the b-spline coefficients of sp(u,v) are determined as the least-squares solution of an ! overdetermined linear system which depends on the value of p and on the values dz(i),i=1,2,3. ! the corresponding sum of squared residuals sq is a simple quadratic function in the variables ! dz(i). these may or may not be provided. the values dz(i) which are not given will be ! determined so as to minimize the resulting sum of squared residuals sq. in that case the user ! must provide some initial guess dz(i) and some estimate (dz(i)-step, dz(i)+step) of the range ! of possible values for these latter. ! ! sp(u,v) also depends on the parameter p (p>0) in such a way that ! - if p tends to infinity, sp(u,v) becomes the least-squares spline with given knots, ! satisfying the constraints. ! - if p tends to zero, sp(u,v) becomes the least-squares polynomial, satisfying the ! constraints. ! - the function f(p)=sumi=1,mu(sumj=1,mv((z(i,j)-sp(u(i),v(j)))**2) is continuous and ! strictly decreasing for p>0. ! pure subroutine fpopdi(ifsu,ifsv,ifbu,ifbv,u,mu,v,mv,z,mz,z0,dz,iopt,ider,tu,nu,tv,nv,& nuest,nvest,p,step,c,nc,fp,fpu,fpv,nru,nrv,wrk,lwrk) ! ! ..scalar arguments.. integer, intent(inout) :: ifsu,ifsv,ifbu,ifbv integer, intent(in) :: mu,mv,mz,nu,nv,nc,lwrk,nuest,nvest real(RKIND), intent(in) :: z0,step real(RKIND), intent(inout) :: fp,p ! ..array arguments.. integer , intent(in) :: ider(2),iopt(3) integer , intent(inout) :: nru(mu),nrv(mv) real(RKIND), intent(in) :: u(mu),v(mv),z(mz),tu(nu),tv(nv) real(RKIND), intent(inout) :: c(nc),dz(3),fpu(nu),fpv(nv),wrk(lwrk) ! ..local scalars.. real(RKIND) res,sq,sqq,step1,step2 integer i,id0,iop0,iop1,i1,j,l,laa,lau,lav1,lav2,lbb,lbu,lbv, & lcc,lcs,lq,lri,lsu,lsv,l1,l2,mm,mvnu,number ! ..local arrays.. integer nr(3) real(RKIND) delta(3),dzz(3),sum(3),a(6,6),g(6) ! we partition the working space lsu = 1 lsv = lsu+4*mu lri = lsv+4*mv mm = max0(nuest,mv+nvest) lq = lri+mm mvnu = nuest*(mv+nvest-8) lau = lq+mvnu lav1 = lau+5*nuest lav2 = lav1+6*nvest lbu = lav2+4*nvest lbv = lbu+5*nuest laa = lbv+5*nvest lbb = laa+2*mv lcc = lbb+2*nvest lcs = lcc+nvest ! we calculate the smoothing spline sp(u,v) according to the input values dz(i),i=1,2,3. iop0 = iopt(2) iop1 = iopt(3) call fpgrdi(ifsu,ifsv,ifbu,ifbv,0,u,mu,v,mv,z,mz,dz,iop0,iop1,tu,nu,tv,nv,p,c,nc,sq,& fp,fpu,fpv,mm,mvnu,wrk(lsu),wrk(lsv),wrk(lri),wrk(lq),wrk(lau),wrk(lav1),& wrk(lav2),wrk(lbu),wrk(lbv),wrk(laa),wrk(lbb),wrk(lcc),wrk(lcs),nru,nrv) id0 = ider(1) if (id0==0) then res = (z0-dz(1))**2 fp = fp+res sq = sq+res endif ! in case all derivative values dz(i) are given (step<=0) or in case we have spline ! interpolation, we accept this spline as a solution. if (step<=zero .or. sq<=zero) return dzz(1:3) = dz(1:3) ! number denotes the number of derivative values dz(i) that still must be optimized. ! let us denote these parameters by g(j),j=1,...,number. number = 0 if (id0<=0) then number = 1 nr (1) = 1 delta(1) = step endif if (iop0/=0 .and. ider(2)==0) then step2 = step*three/tu(5) nr(number+1) = 2 nr(number+2) = 3 delta(number+1) = step2 delta(number+2) = step2 number = number+2 endif if(number==0) return ! the sum of squared residuals sq is a quadratic polynomial in the parameters g(j). we ! determine the unknown coefficients of this polymomial by calculating (number+1)*(number+2)/2 ! different splines according to specific values for g(j). parameter_splines: do i=1,number l = nr(i) step1 = delta(i) dzz(l) = dz(l)+step1 call fpgrdi(ifsu,ifsv,ifbu,ifbv,1,u,mu,v,mv,z,mz,dzz,iop0,iop1,tu,nu,tv,nv,p,c,nc,sum(i),& fp,fpu,fpv,mm,mvnu,wrk(lsu),wrk(lsv),wrk(lri),wrk(lq),wrk(lau),wrk(lav1), & wrk(lav2),wrk(lbu),wrk(lbv),wrk(laa),wrk(lbb),wrk(lcc),wrk(lcs),nru,nrv) if (id0==0) sum(i) = sum(i)+(z0-dzz(1))**2 dzz(l) = dz(l)-step1 call fpgrdi(ifsu,ifsv,ifbu,ifbv,1,u,mu,v,mv,z,mz,dzz,iop0,iop1,tu,nu,tv,nv,p,c,nc,sqq,& fp,fpu,fpv,mm,mvnu,wrk(lsu),wrk(lsv),wrk(lri),wrk(lq),wrk(lau),wrk(lav1), & wrk(lav2),wrk(lbu),wrk(lbv),wrk(laa),wrk(lbb),wrk(lcc),wrk(lcs),nru,nrv) if (id0==0) sqq = sqq+(z0-dzz(1))**2 a(i,i) = (sum(i)+sqq-sq-sq)/step1**2 if (a(i,i)<=zero) then number = 0 exit parameter_splines endif g(i) = (sqq-sum(i))/(step1+step1) dzz(l) = dz(l) end do parameter_splines if (number>1) then do i=2,number l1 = nr(i) step1 = delta(i) dzz(l1) = dz(l1)+step1 i1 = i-1 do j=1,i1 l2 = nr(j) step2 = delta(j) dzz(l2) = dz(l2)+step2 call fpgrdi(ifsu,ifsv,ifbu,ifbv,1,u,mu,v,mv,z,mz,dzz,iop0,iop1,tu,nu,tv,nv,p,c,nc,sqq,& fp,fpu,fpv,mm,mvnu,wrk(lsu),wrk(lsv),wrk(lri),wrk(lq),wrk(lau),wrk(lav1), & wrk(lav2),wrk(lbu),wrk(lbv),wrk(laa),wrk(lbb),wrk(lcc),wrk(lcs),nru,nrv) if (id0==0) sqq = sqq+(z0-dzz(1))**2 a (i,j) = (sq+sqq-sum(i)-sum(j))/(step1*step2) dzz(l2) = dz(l2) end do dzz(l1) = dz(l1) end do endif ! the optimal values g(j) are found as the solution of the system ! d (sq) / d (g(j)) = 0 , j=1,...,number. if (number>0) then call fpsysy(a,number,g) do i=1,number l = nr(i) dz(l) = dz(l)+g(i) end do endif ! we determine the spline sp(u,v) according to the optimal values g(j). call fpgrdi(ifsu,ifsv,ifbu,ifbv,0,u,mu,v,mv,z,mz,dz,iop0,iop1,tu,nu,tv,nv,p,c,nc,sq, & fp,fpu,fpv,mm,mvnu,wrk(lsu),wrk(lsv),wrk(lri),wrk(lq),wrk(lau),wrk(lav1), & wrk(lav2),wrk(lbu),wrk(lbv),wrk(laa),wrk(lbb),wrk(lcc),wrk(lcs),nru,nrv) if (id0==0) fp = fp+(z0-dz(1))**2 return end subroutine fpopdi pure subroutine fpopsp(ifsu,ifsv,ifbu,ifbv,u,mu,v,mv,r, & mr,r0,r1,dr,iopt,ider,tu,nu,tv,nv,nuest,nvest,p,step,c,nc, & fp,fpu,fpv,nru,nrv,wrk,lwrk) ! given the set of function values r(i,j) defined on the rectangular ! grid (u(i),v(j)),i=1,2,...,mu;j=1,2,...,mv, fpopsp determines a ! smooth bicubic spline approximation with given knots tu(i),i=1,..,nu ! in the u-direction and tv(j),j=1,2,...,nv in the v-direction. this ! spline sp(u,v) will be periodic in the variable v and will satisfy ! the following constraints ! ! s(tu(1),v) = dr(1) , tv(4) <=v<= tv(nv-3) ! ! s(tu(nu),v) = dr(4) , tv(4) <=v<= tv(nv-3) ! ! and (if iopt(2) = 1) ! ! d s(tu(1),v) ! ------------ = dr(2)*cos(v)+dr(3)*sin(v) , tv(4) <=v<= tv(nv-3) ! d u ! ! and (if iopt(3) = 1) ! ! d s(tu(nu),v) ! ------------- = dr(5)*cos(v)+dr(6)*sin(v) , tv(4) <=v<= tv(nv-3) ! d u ! ! where the parameters dr(i) correspond to the derivative values at the ! poles as defined in subroutine spgrid. ! ! the b-spline coefficients of sp(u,v) are determined as the least- ! squares solution of an overdetermined linear system which depends ! on the value of p and on the values dr(i),i=1,...,6. the correspond- ! ing sum of squared residuals sq is a simple quadratic function in ! the variables dr(i). these may or may not be provided. the values ! dr(i) which are not given will be determined so as to minimize the ! resulting sum of squared residuals sq. in that case the user must ! provide some initial guess dr(i) and some estimate (dr(i)-step, ! dr(i)+step) of the range of possible values for these latter. ! ! sp(u,v) also depends on the parameter p (p>0) in such a way that ! - if p tends to infinity, sp(u,v) becomes the least-squares spline ! with given knots, satisfying the constraints. ! - if p tends to zero, sp(u,v) becomes the least-squares polynomial, ! satisfying the constraints. ! - the function f(p)=sumi=1,mu(sumj=1,mv((r(i,j)-sp(u(i),v(j)))**2) ! is continuous and strictly decreasing for p>0. ! ! ..scalar arguments.. integer, intent(in) :: mu,mv,mr,nu,nv,nuest,nvest,nc,lwrk integer, intent(inout) :: ifsu,ifsv,ifbu,ifbv real(RKIND), intent(in) :: r0,r1 real(RKIND), intent(inout) :: fp real(RKIND), intent(out) :: p ! ..array arguments.. integer, intent(in) :: ider(4),iopt(3) integer, intent(inout) :: nru(mu),nrv(mv) real(RKIND), intent(in) :: u(mu),v(mv),r(mr),tu(nu),tv(nv) real(RKIND), intent(inout) :: dr(6),wrk(lwrk),step(2),c(nc),fpu(nu),fpv(nv) ! ..local scalars.. real(RKIND) :: sq,sqq,sq0,sq1,step1,step2 integer :: i,id0,iop0,iop1,i1,j,l,lau,lav1,lav2,la0,la1,lbu,lbv,lb0, & lb1,lc0,lc1,lcs,lq,lri,lsu,lsv,l1,l2,mm,mvnu,number, id1 ! ..local arrays.. integer :: nr(6) logical :: zeroed real(RKIND) :: delta(6),drr(6),sum(6),a(6,6),g(6) ! we partition the working space lsu = 1 lsv = lsu+4*mu lri = lsv+4*mv mm = max(nuest,mv+nvest) lq = lri+mm mvnu = nuest*(mv+nvest-8) lau = lq+mvnu lav1 = lau+5*nuest lav2 = lav1+6*nvest lbu = lav2+4*nvest lbv = lbu+5*nuest la0 = lbv+5*nvest la1 = la0+2*mv lb0 = la1+2*mv lb1 = lb0+2*nvest lc0 = lb1+2*nvest lc1 = lc0+nvest lcs = lc1+nvest ! we calculate the smoothing spline sp(u,v) according to the input ! values dr(i),i=1,...,6. iop0 = iopt(2) iop1 = iopt(3) id0 = ider(1) id1 = ider(3) call fpgrsp(ifsu,ifsv,ifbu,ifbv,0,u,mu,v,mv,r,mr,dr, & iop0,iop1,tu,nu,tv,nv,p,c,nc,sq,fp,fpu,fpv,mm,mvnu, & wrk(lsu),wrk(lsv),wrk(lri),wrk(lq),wrk(lau),wrk(lav1), & wrk(lav2),wrk(lbu),wrk(lbv),wrk(la0),wrk(la1),wrk(lb0), & wrk(lb1),wrk(lc0),wrk(lc1),wrk(lcs),nru,nrv) sq0 = merge((r0-dr(1))**2,zero,id0==0) sq1 = merge((r1-dr(4))**2,zero,id1==0) sq = sq+sq0+sq1 ! in case all derivative values dr(i) are given (step<=0) or in case ! we have spline interpolation, we accept this spline as a solution. if (sq<=zero .or. all(step(1:2)<=zero)) return drr = dr ! number denotes the number of derivative values dr(i) that still must ! be optimized. let us denote these parameters by g(j),j=1,...,number. number = 0 if (id0<=0) then number = 1 nr (1) = 1 delta(1) = step(1) endif if (iop0/=0 .and. ider(2)==0) then step2 = step(1)*three/(tu(5)-tu(4)) nr (number+1:number+2) = [2,3] delta(number+1:number+2) = step2 number = number+2 endif if (id1<=0) then number = number+1 nr (number) = 4 delta(number) = step(2) end if if (iop1/=0 .and. ider(4)==0) then step2 = step(2)*three/(tu(nu)-tu(nu-4)) nr (number+1:number+2) = [5,6] delta(number+1:number+2) = step2 number = number+2 endif if(number==0) return ! the sum of squared residulas sq is a quadratic polynomial in the parameters g(j). ! we determine the unknown coefficients of this polymomial by calculating (number+1)*(number+2)/2 ! different splines according to specific values for g(j). zeroed = .false. do i=1,number l = nr(i) step1 = delta(i) drr(l) = dr(l)+step1 call fpgrsp(ifsu,ifsv,ifbu,ifbv,1,u,mu,v,mv,r,mr,drr, & iop0,iop1,tu,nu,tv,nv,p,c,nc,sum(i),fp,fpu,fpv,mm,mvnu, & wrk(lsu),wrk(lsv),wrk(lri),wrk(lq),wrk(lau),wrk(lav1), & wrk(lav2),wrk(lbu),wrk(lbv),wrk(la0),wrk(la1),wrk(lb0), & wrk(lb1),wrk(lc0),wrk(lc1),wrk(lcs),nru,nrv) if (id0==0) sq0 = (r0-drr(1))**2 if (id1==0) sq1 = (r1-drr(4))**2 sum(i) = sum(i)+sq0+sq1 drr(l) = dr(l)-step1 call fpgrsp(ifsu,ifsv,ifbu,ifbv,1,u,mu,v,mv,r,mr,drr, & iop0,iop1,tu,nu,tv,nv,p,c,nc,sqq,fp,fpu,fpv,mm,mvnu, & wrk(lsu),wrk(lsv),wrk(lri),wrk(lq),wrk(lau),wrk(lav1), & wrk(lav2),wrk(lbu),wrk(lbv),wrk(la0),wrk(la1),wrk(lb0), & wrk(lb1),wrk(lc0),wrk(lc1),wrk(lcs),nru,nrv) if (id0==0) sq0 = (r0-drr(1))**2 if (id1==0) sq1 = (r1-drr(4))**2 sqq = sqq+sq0+sq1 drr(l) = dr(l) a(i,i) = (sum(i)+sqq-sq-sq)/step1**2 if (a(i,i)<=zero) then zeroed = .true. exit end if g(i) = (sqq-sum(i))/(2*step1) end do if (.not.zeroed) then if (number>1) then do i=2,number l1 = nr(i) step1 = delta(i) drr(l1) = dr(l1)+step1 i1 = i-1 do j=1,i1 l2 = nr(j) step2 = delta(j) drr(l2) = dr(l2)+step2 call fpgrsp(ifsu,ifsv,ifbu,ifbv,1,u,mu,v,mv,r,mr,drr, & iop0,iop1,tu,nu,tv,nv,p,c,nc,sqq,fp,fpu,fpv,mm,mvnu, & wrk(lsu),wrk(lsv),wrk(lri),wrk(lq),wrk(lau),wrk(lav1), & wrk(lav2),wrk(lbu),wrk(lbv),wrk(la0),wrk(la1),wrk(lb0), & wrk(lb1),wrk(lc0),wrk(lc1),wrk(lcs),nru,nrv) if(id0==0) sq0 = (r0-drr(1))**2 if(id1==0) sq1 = (r1-drr(4))**2 sqq = sqq+sq0+sq1 a(i,j) = (sq+sqq-sum(i)-sum(j))/(step1*step2) drr(l2) = dr(l2) end do drr(l1) = dr(l1) end do end if ! the optimal values g(j) are found as the solution of the system ! d (sq) / d (g(j)) = 0 , j=1,...,number. call fpsysy(a,number,g) forall(i=1:number) dr(nr(i))=dr(nr(i))+g(i) endif ! we determine the spline sp(u,v) according to the optimal values g(j). call fpgrsp(ifsu,ifsv,ifbu,ifbv,0,u,mu,v,mv,r,mr,dr, & iop0,iop1,tu,nu,tv,nv,p,c,nc,sq,fp,fpu,fpv,mm,mvnu, & wrk(lsu),wrk(lsv),wrk(lri),wrk(lq),wrk(lau),wrk(lav1), & wrk(lav2),wrk(lbu),wrk(lbv),wrk(la0),wrk(la1),wrk(lb0), & wrk(lb1),wrk(lc0),wrk(lc1),wrk(lcs),nru,nrv) if(id0==0) sq0 = (r0-dr(1))**2 if(id1==0) sq1 = (r1-dr(4))**2 sq = sq+sq0+sq1 return end subroutine fpopsp ! subroutine fporde sorts the data points (x(i),y(i)),i=1,2,...,m according to the panel ! tx(l)<=x<tx(l+1),ty(k)<=y<ty(k+1), they belong to. for each panel a stack is constructed ! containing the numbers of data points lying inside; index(j),j=1,2,...,nreg points to the first ! data point in the jth panel while nummer(i),i=1,2,...,m gives the number of the next data point ! in the panel. pure subroutine fporde(x,y,m,kx,ky,tx,nx,ty,ny,nummer,index,nreg) ! ..scalar arguments.. integer, intent(in) :: m,kx,ky,nx,ny,nreg ! ..array arguments.. real(RKIND), intent(in) :: x(m),y(m),tx(nx),ty(ny) integer, intent(out) :: nummer(m),index(nreg) ! ..local scalars.. real(RKIND) :: xi,yi integer :: im,k,kx1,ky1,k1,l,l1,nk1x,nk1y,num,nyy ! .. kx1 = kx+1 ky1 = ky+1 nk1x = nx-kx1 nk1y = ny-ky1 nyy = nk1y-ky index = 0 points: do im=1,m xi = x(im) yi = y(im) l = kx1 l1 = l+1 do while (.not.(xi<tx(l1) .or. l==nk1x)) l = l1 l1 = l+1 end do k = ky1 k1 = k+1 do while (.not.(yi<ty(k1) .or. k==nk1y)) k = k1 k1 = k+1 end do num = (l-kx1)*nyy+k-ky nummer(im) = index(num) index(num) = im end do points return end subroutine fporde pure subroutine fppara(iopt,idim,m,u,mx,x,w,ub,ue,k,s,nest,tol,maxit, & k1,k2,n,t,nc,c,fp,fpint,z,a,b,g,q,nrdata,ier) ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: ub,ue,s,tol real(RKIND), intent(inout) :: fp integer, intent(in) :: idim,maxit,iopt,m,mx,k,k1,k2,nest integer, intent(inout) :: n,nc,ier ! ..array arguments.. real(RKIND), intent(in) :: u(m),x(mx),w(m) real(RKIND), intent(inout) :: t(nest),c(nc),fpint(nest),z(nc),a(nest,k1),b(nest,k2),g(nest,k2),q(m,k1) integer, intent(inout) :: nrdata(nest) ! ..local scalars.. real(RKIND) :: acc,cos,fac,fpart,fpms,fpold,fp0,f1,f2,f3,p,pinv,piv,p1,p2,p3,rn,sin,store,term,ui,wi integer :: i,it,iter,i1,i2,i3,j,jj,j1,j2,k3,l,l0,mk1,nk1,nmax,nmin,nplus,npl1,nrint,n8 logical :: new,check1,check3,success ! ..local arrays.. real(RKIND) :: h(MAX_ORDER+1),xi(idim) fpold = zero fp0 = zero nplus = 0 ! ***** ! part 1: determination of the number of knots and their position ! ***** ! given a set of knots we compute the least-squares curve sinf(u), and the corresponding sum ! of squared residuals fp=f(p=inf). ! if iopt=-1 sinf(u) is the requested curve. ! if iopt=0 or iopt=1 we check whether we can accept the knots: ! if fp <=s we will continue with the current set of knots. ! if fp > s we will increase the number of knots and compute the corresponding least- ! squares curve until finally fp<=s. ! the initial choice of knots depends on the value of s and iopt. ! if s=0 we have spline interpolation; in that case the number of knots equals nmax = m+k+1. ! if (s>0 and iopt=0) we first compute the least-squares polynomial curve of degree k; ! n = nmin = 2*k+2 ! iopt=1 we start with the set of knots found at the last call of the routine, except for ! the case that s > fp0; then we compute directly the polynomial curve of degree k. ! ***** ! determine nmin, the number of knots for polynomial approximation. nmin = 2*k1 bootstrap: if (iopt>=0) then ! calculation of acc, the absolute tolerance for the root of f(p)=s. acc = tol*s ! determine nmax, the number of knots for spline interpolation. nmax = m+k1 interpolating: if (s<=zero) then ! if s=0, s(u) is an interpolating curve. ! check that the required storage space exceeds the available one. n = nmax if (nmax>nest) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! find the position of the interior knots in case of interpolation. mk1 = m-k1 if (mk1/=0) then k3 = k/2 i = k2 j = k3+1 odd_k: if (k3*2/=k) then t(k2:k2+mk1-1) = u(j+1:j+mk1) else odd_k t(k2:k2+mk1-1) = half*( u(j+1:j+mk1) + u(j:j+mk1-1) ) endif odd_k endif else interpolating ! if s>0 our initial choice of knots depends on the value of iopt. ! if iopt=0 or iopt=1 and s>=fp0, we start computing the least-squares ! polynomial curve which is a spline curve without interior knots. ! if iopt=1 and fp0>s we start computing the least squares spline curve ! according to the set of knots found at the last call of the routine. use_last_call: if (iopt/=0 .and. n/=nmin) then fp0 = fpint(n) fpold = fpint(n-1) nplus = nrdata(n) endif use_last_call if (fp0<=s .or. iopt==0 .or. n==nmin) then n = nmin fpold = zero nplus = 0 nrdata(1) = m-2 endif endif interpolating endif bootstrap ! main loop for the different sets of knots. m is a safe upper bound ! for the number of trials. iter = 0 main_loop: do while (iter<=m) iter = iter+1 if (n==nmin) ier = FITPACK_LEASTSQUARES_OK ! find nrint, tne number of knot intervals. nrint = n-nmin+1 ! find the position of the additional knots which are needed for ! the b-spline representation of s(u). nk1 = n-k1 t(1:k1) = ub t(n-k:n) = ue ! compute the b-spline coefficients of the least-squares spline curve ! sinf(u). the observation matrix a is built up row by row and ! reduced to upper triangular form by givens transformations. ! at the same time fp=f(p=inf) is computed. fp = zero ! initialize the b-spline coefficients and the observation matrix a. z(1:nc) = zero a(1:nk1,1:k1) = zero l = k1 coefs: do it=1,m ! fetch the current data point u(it),x(it). ui = u(it) wi = w(it) xi = x((it-1)*idim+1:it*idim)*wi ! search for knot interval t(l) <= ui < t(l+1). do while (ui>=t(l+1) .and. l/=nk1) l = l+1 end do ! evaluate the (k+1) non-zero b-splines at ui and store them in q. h = fpbspl(t,n,k,ui,l) q(it,1:k1) = h(1:k1) h(:k1) = wi*h(:k1) ! rotate the new row of the observation matrix into triangle. j = l-k1 rotate_row: do i=1,k1 j = j+1 piv = h(i); if (equal(piv,zero)) cycle rotate_row ! calculate the parameters of the givens transformation. call fpgivs(piv,a(j,1),cos,sin) ! transformations to right hand side. call fprota(cos,sin,xi,z(j:j+(idim-1)*n:n)) ! transformations to left hand side. not_last: if (i<k1) then i2 = 1 i3 = i+1 do i1 = i3,k1 i2 = i2+1 call fprota(cos,sin,h(i1),a(j,i2)) end do endif not_last end do rotate_row ! add contribution of this row to the sum of squares of residual right hand sides. fp = fp + sum(xi**2) end do coefs if (ier==FITPACK_LEASTSQUARES_OK) fp0 = fp fpint(n-1:n) = [fpold,fp0] nrdata(n) = nplus ! backward substitution to obtain the b-spline coefficients. j1 = 1 do j2=1,idim c(j1:j1+nk1-1) = fpback(a,z(j1),nk1,k1,nest) j1 = j1+n end do ! test whether the approximation sinf(u) is an acceptable solution. if (iopt<0) return ! was done already fpms = fp-s; if(abs(fpms)<acc) return ! if f(p=inf) < s accept the choice of knots. if (fpms<zero) exit main_loop ! if n = nmax, sinf(u) is an interpolating spline curve. if (n==nmax) then ier = FITPACK_INTERPOLATING_OK return endif ! increase the number of knots. ! if n=nest we cannot increase the number of knots because of the storage capacity limitation. if (n==nest) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! determine the number of knots nplus we are going to add. if (ier==FITPACK_OK) then npl1 = nplus*2 rn = nplus if (fpold-fp>acc) npl1 = int(rn*fpms/(fpold-fp)) nplus = min(nplus*2,max(npl1,nplus/2,1)) else nplus = 1 ier = FITPACK_OK endif ! Initialize iterate fpold = fp ! compute the sum of squared residuals for each knot interval ! t(j+k) <= u(i) <= t(j+k+1) and store it in fpint(j),j=1,2,...nrint. fpart = zero i = 1 l = k2 new = .false. jj = 0 square_residuals: do it=1,m if (u(it)>=t(l) .and. l<=nk1) then new = .true. l = l+1 endif term = zero l0 = l-k2 do j2=1,idim fac = dot_product(c(l0+1:l0+k1),q(it,1:k1)) jj = jj+1 term = term+(w(it)*(fac-x(jj)))**2 l0 = l0+n end do fpart = fpart+term if (new) then store = term*half fpint(i) = fpart-store i = i+1 fpart = store new = .false. endif end do square_residuals fpint(nrint) = fpart add_new_knots: do l=1,nplus ! add a new knot. call fpknot(u,m,t,n,fpint,nrdata,nrint,nest,1) ! if n=nmax we locate the knots as for interpolation if (n==nmax) then ! find the position of the interior knots in case of interpolation. mk1 = m-k1 if (mk1/=0) then k3 = k/2 i = k2 j = k3+2 do jj=1,mk1 t(i) = merge( u(j) , (u(j)+u(j-1))*half , k3*2/=k) i = i+1 j = j+1 end do endif ! Restart main loop iter = 0 cycle main_loop end if ! test whether we cannot further increase the number of knots. if (n==nest) exit add_new_knots end do add_new_knots ! restart the computations with the new set of knots. end do main_loop ! test whether the least-squares kth degree polynomial curve is a solution ! of our approximation problem. if (ier==FITPACK_LEASTSQUARES_OK) return ! ***** ! part 2: determination of the smoothing spline curve sp(u). ! ***** ! we have determined the number of knots and their position. ! we now compute the b-spline coefficients of the smoothing curve sp(u). the observation matrix a ! is extended by the rows of matrix b expressing that the kth derivative discontinuities of sp(u) ! at the interior knots t(k+2),...t(n-k-1) must be zero. the corresponding weights of these ! additional rows are set to 1/p. ! iteratively we then have to determine the value of p such that f(p), the sum of squared ! residuals be = s. we already know that the least squares kth degree polynomial curve corresponds ! to p=0, and that the least-squares spline curve corresponds to p=infinity. the iteration process ! which is proposed here, makes use of rational interpolation. since f(p) is a convex and strictly ! decreasing function of p, it can be approximated by a rational function r(p) = (u*p+v)/(p+w). ! three values of p(p1,p2,p3) with corresponding values of f(p) (f1=f(p1)-s,f2=f(p2)-s,f3=f(p3)-s) ! are used to calculate the new value of p such that r(p)=s. convergence is guaranteed by taking ! f1>0 and f3<zero ! ***** ! evaluate the discontinuity jump of the kth derivative of the b-splines at the knots ! t(l),l=k+2,...n-k-1 and store in b. call fpdisc(t,n,k2,b,nest) ! initial value for p. p1 = zero f1 = fp0-s p3 = -one f3 = fpms p = sum(a(1:nk1,1))/nk1 check1 = .false. check3 = .false. n8 = n-nmin ! iteration process to find the root of f(p) = s. iter = 0 find_root: do while (iter<maxit) iter = iter+1 ! the rows of matrix b with weight 1/p are rotated into the ! triangularised observation matrix a which is stored in g. pinv = one/p c(1:nc) = z(1:nc) g(1:nk1,1:k1) = a(1:nk1,1:k1) g(1:nk1,k2) = zero b_rows: do it=1,n8 ! the row of matrix b is rotated into triangle by givens transformation h(1:k2) = b(it,1:k2)*pinv xi = zero b_cols: do j=it,nk1 piv = h(1) ! calculate the parameters of the givens transformation. call fpgivs(piv,g(j,1),cos,sin) ! transformations to right hand side. call fprota(cos,sin,xi,c(j:j+(idim-1)*n:n)) if (j==nk1) cycle b_rows ! transformations to left hand side. i2 = merge(nk1-j,k1,j>n8)+1 call fprota(cos,sin,h(2:i2),g(j,2:i2)) h(1:i2) = [h(2:i2),zero] end do b_cols end do b_rows ! backward substitution to obtain the b-spline coefficients. j1 = 1 do j2=1,idim c(j1:j1+nk1-1) = fpback(g,c(j1),nk1,k2,nest) j1 =j1+n end do ! computation of f(p). fp = zero l = k2 jj = 0 get_fp: do it=1,m if (u(it)>=t(l) .and. l<=nk1) l = l+1 l0 = l-k2 term = zero do j2=1,idim fac = dot_product(c(l0+1:l0+k1),q(it,1:k1)) jj = jj+1 term = term+(fac-x(jj))**2 l0 = l0+n end do fp = fp+term*w(it)**2 end do get_fp ! SUCCESS! the approximation sp(u) is an acceptable solution. fpms = fp-s; if (abs(fpms)<acc) return ! find the new value of p and carry out one more step. call root_finding_iterate(p1,f1,p2,f2,p3,f3,p,fpms,acc,check1,check3,success) if (.not.success) then ier = FITPACK_S_TOO_SMALL return end if end do find_root ! Maximum number of iterations reached ier = FITPACK_MAXIT return end subroutine fppara pure subroutine fppasu(iopt,ipar,idim,u,mu,v,mv,z,mz,s,nuest,nvest, & tol,maxit,nc,nu,tu,nv,tv,c,fp,fp0,fpold,reducu,reducv,fpintu, & fpintv,lastdi,nplusu,nplusv,nru,nrv,nrdatu,nrdatv,wrk,lwrk,ier) ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: s,tol real(RKIND), intent(inout) :: fp,fp0,fpold,reducu,reducv integer , intent(in) :: iopt,idim,mu,mv,mz,nuest,nvest,maxit integer , intent(inout) :: nc,nu,nv,lastdi,nplusu,nplusv,lwrk,ier ! ..array arguments.. real(RKIND), intent(in) :: u(mu),v(mv),z(mz*idim) real(RKIND), intent(inout) :: tu(nuest),tv(nvest),c(nc*idim),fpintu(nuest),fpintv(nvest),wrk(lwrk) integer , intent(in) :: ipar(2) integer , intent(inout) :: nrdatu(nuest),nrdatv(nvest),nru(mu),nrv(mv) ! ..local scalars real(RKIND) :: acc,fpms,f1,f2,f3,p,p1,p2,p3,rn,peru,perv,ub,ue,vb,ve integer :: i,ifbu,ifbv,ifsu,ifsv,iter,j,lau1,lav1,laa,l,lau,lav,lbu,lbv,lq,lri,lsu,& lsv,mm,mpm,mvnu,ncof,nk1u,nk1v,nmaxu,nmaxv,nminu,nminv,nplu,nplv,npl1,& nrintu,nrintv,nue,nuk,nve,nuu,nvv logical :: periodic_u,periodic_v,check1,check3,success ! acc denotes the absolute tolerance for the root of f(p)=s. acc = tol*s ! find nmaxu and nmaxv which denote the number of knots in u- and v- ! direction in case of spline interpolation. nmaxu = mu+4+2*ipar(1) nmaxv = mv+4+2*ipar(2) periodic_u = ipar(1)==1 periodic_v = ipar(2)==1 ! find nue and nve which denote the maximum number of knots ! allowed in each direction nue = min(nmaxu,nuest) nve = min(nmaxv,nvest) ! set boundaries of the approximation domain ub = u(1) ue = u(mu) vb = v(1) ve = v(mv) ! we partition the working space. lsu = 1 lsv = lsu+mu*4 lri = lsv+mv*4 mm = max(nuest,mv) lq = lri+mm*idim mvnu = nuest*mv*idim lau = lq+mvnu nuk = nuest*5 lbu = lau+nuk lav = lbu+nuk nuk = nvest*5 lbv = lav+nuk laa = lbv+nuk lau1 = lau if (ipar(1)/=0) then peru = ue-ub lau1 = laa laa = laa+4*nuest endif lav1 = lav if (ipar(2)/=0) then perv = ve-vb lav1 = laa endif peru = merge(ue-ub,zero,periodic_u) perv = merge(ve-vb,zero,periodic_v) ! ********************************************************************************************** ! part 1: determination of the number of knots and their position ! ********************************************************************************************** ! given a set of knots we compute the least-squares closed curve sinf(u,v) ,and the ! corresponding sum of squared residuals fp=f(p=inf). ! if iopt=-1 sinf(u,v) is the requested approximation ! if iopt=0 or iopt=1 we check whether we can accept the knots: ! if fp <=s we will continue with the current set of knots. ! if fp > s we will increase the number of knots and compute the orresponding least-squares ! spline until finally fp<=s. ! the initial choice of knots depends on the value of s and iopt. if s=0 we have spline ! interpolation; in that case the number of knots equals nmaxu = mu+4+2*ipar(1) and ! nmaxv = mv+4+2*ipar(2). ! if s > 0 and iopt=0 we first compute the least-squares polynomial nu=nminu=8, nv=nminv=8 ! if iopt=1 we start with the set of knots found at the last call of the routine, except for ! the case that s > fp0; then we compute directly the least-squares polynomial directly. ! ********************************************************************************************** ! determine the number of knots for polynomial approximation. nminu = 8 nminv = 8 bootstrap: if (iopt>=0) then interpolating: if (s<=zero) then ! if s = 0, s(u,v) is an interpolating spline. nu = nmaxu nv = nmaxv ! test whether the required storage space exceeds the available one. if (nv>nvest .or. nu>nuest) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! find the position of the interior knots in case of interpolation: ! the knots in the u-direction. nuu = nu-8 if (nuu>0) tu(5:4+nuu) = u(3-ipar(1):2-ipar(1)+nuu) ! the knots in the v-direction. nvv = nv-8 if (nvv>0) tv(5:4+nvv) = v(3-ipar(2):2-ipar(2)+nvv) else interpolating ! if s > 0 our initial choice of knots depends on the value of iopt. use_last_call: if (iopt/=0 .and. fp0>s) then ! if iopt=1 and fp0 > s we start computing the least- squares spline ! according to the set of knots found at the last call of the routine. ! we determine the number of grid coordinates u(i) inside each knot ! interval (tu(l),tu(l+1)). l = 5 j = 1 nrdatu(1) = 0 mpm = mu-1 do i=2,mpm nrdatu(j) = nrdatu(j)+1 if (u(i)>=tu(l)) then nrdatu(j) = nrdatu(j)-1 l = l+1 j = j+1 nrdatu(j) = 0 endif end do ! we determine the number of grid coordinates v(i) inside each knot ! interval (tv(l),tv(l+1)). l = 5 j = 1 nrdatv(1) = 0 mpm = mv-1 do i=2,mpm nrdatv(j) = nrdatv(j)+1 if (v(i)>=tv(l)) then nrdatv(j) = nrdatv(j)-1 l = l+1 j = j+1 nrdatv(j) = 0 endif end do else ! if iopt=0 or iopt=1 and s>=fp0, we start computing the least-squares ! polynomial (which is a spline without interior knots). nu = nminu nv = nminv nrdatu(1) = mu-2 nrdatv(1) = mv-2 lastdi = KNOT_DIM_NONE nplusu = 0 nplusv = 0 fp0 = zero fpold = zero reducu = zero reducv = zero endif use_last_call endif interpolating endif bootstrap mpm = mu+mv ifsu = 0 ifsv = 0 ifbu = 0 ifbv = 0 p = -one ! main loop for the different sets of knots. mpm=mu+mv is a safe upper ! bound for the number of trials. iter = 0 main_loop: do while (iter<=mpm) iter = iter+1 if (nu==nminu .and. nv==nminv) ier = FITPACK_LEASTSQUARES_OK ! find nrintu (nrintv) which is the number of knot intervals in the ! u-direction (v-direction). nrintu = nu-nminu+1 nrintv = nv-nminv+1 ! find ncof, the number of b-spline coefficients for the current set of knots. nk1u = nu-4 nk1v = nv-4 ncof = nk1u*nk1v ! find the position of the additional knots which are needed for the ! b-spline representation of s(u,v). if (periodic_u) then tu(1:4) = [tu(nu-6:nu-4)-peru,ub] tu(nu-3:nu) = [ue,tu(5:7)+peru] else tu(1:4) = ub tu(nu-3:nu) = ue endif if (periodic_v) then tv(1:4) = [tv(nv-6:nv-4)-perv,vb] tv(nv-3:nv) = [ve,tv(5:7)+perv] else tv(1:4) = vb tv(nv-3:nv) = ve end if ! find the least-squares spline sinf(u,v) and calculate for each knot interval ! tu(j+3)<=u<=tu(j+4) (tv(j+3)<=v<=tv(j+4)) the sum of squared residuals ! fpintu(j),j=1,2,...,nu-7 (fpintv(j),j=1,2,...,nv-7) for the data points having their ! absciss (ordinate)-value belonging to that interval. ! fp gives the total sum of squared residuals. call fpgrpa(ifsu,ifsv,ifbu,ifbv,idim,ipar,u,mu,v,mv,z,mz,tu, & nu,tv,nv,p,c,nc,fp,fpintu,fpintv,mm,mvnu,wrk(lsu),wrk(lsv), & wrk(lri),wrk(lq),wrk(lau),wrk(lau1),wrk(lav),wrk(lav1), & wrk(lbu),wrk(lbv),nru,nrv) if (ier==FITPACK_LEASTSQUARES_OK) fp0 = fp ! SUCCESS! the least-squares spline is an acceptable solution. fpms = fp-s if (iopt<0 .or. abs(fpms)<acc) return ! if f(p=inf) < s, we accept the choice of knots. if (fpms<zero) exit main_loop ! if nu=nmaxu and nv=nmaxv, sinf(u,v) is an interpolating spline. if (nu==nmaxu .and. nv==nmaxv) then ier = FITPACK_INTERPOLATING_OK fp = zero return end if ! increase the number of knots. ! if nu=nue and nv=nve we cannot further increase the number of knots ! because of the storage capacity limitation. if (nu==nue .and. nv==nve) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ier = FITPACK_OK ! adjust the parameter reducu or reducv according to the direction ! in which the last added knots were located. select case (lastdi) case (KNOT_DIM_1); reducu = fpold-fp case (KNOT_DIM_2); reducv = fpold-fp end select ! store the sum of squared residuals for the current set of knots. fpold = fp ! find nplu, the number of knots we should add in the u-direction. nplu = 1 if (nu/=nminu) then npl1 = nplusu*2 rn = nplusu if (reducu>acc) npl1 = int(rn*fpms/reducu) nplu = min(nplusu*2,max(npl1,nplusu/2,1)) endif ! find nplv, the number of knots we should add in the v-direction. nplv = 1 if (nv/=nminv) then npl1 = nplusv*2 rn = nplusv if (reducv>acc) npl1 = int(rn*fpms/reducv) nplv = min(nplusv*2,max(npl1,nplusv/2,1)) endif ! test whether we are going to add knots in the u- or v-direction. ! lastdi = last knot direction: lastdi==0 = not yet set ! lastdi==1 = v direction ! lastdi==-1 = u direction lastdi = new_knot_dimension(nu,nplu,nue,nv,nplv,nve,lastdi) choose_dir: if (lastdi==KNOT_DIM_2) then ! addition in the v-direction. nplusv = nplv ifsv = 0 add_v_knots: do l=1,nplusv ! add a new knot in the v-direction. call fpknot(v,mv,tv,nv,fpintv,nrdatv,nrintv,nvest,1) ! test whether we cannot further increase the number of knots in the v-direction. if (nv==nve) exit add_v_knots end do add_v_knots else choose_dir ! addition in the u-direction. nplusu = nplu ifsu = 0 add_u_knots: do l=1,nplusu ! add a new knot in the u-direction call fpknot(u,mu,tu,nu,fpintu,nrdatu,nrintu,nuest,1) ! test whether we cannot further increase the number of knots in the u-direction. if (nu==nue) exit add_u_knots end do add_u_knots endif choose_dir ! restart the computations with the new set of knots. end do main_loop ! test whether the least-squares polynomial is a solution of our ! approximation problem. if (ier==FITPACK_LEASTSQUARES_OK) return ! ********************************************************************************************** ! part 2: determination of the smoothing spline sp(u,v) ! ********************************************************************************************** ! we have determined the number of knots and their position. we now compute the b-spline ! coefficients of the smoothing spline sp(u,v). this smoothing spline varies with the parameter ! p in such a way that ! f(p)= suml=1,idim(sumi=1,mu(sumj=1,mv((z(i,j,l)-sp(u(i),v(j),l))**2) ! is a continuous, strictly decreasing function of p. moreover the least-squares polynomial ! corresponds to p=0 and the least-squares spline to p=infinity. iteratively we then have to ! determine the positive value of p such that f(p)=s. the process which is proposed here makes ! use of rational interpolation. f(p) is approximated by a rational function r(p)=(u*p+v)/(p+w); ! three values of p (p1,p2,p3) with corresponding values of f(p) (f1=f(p1)-s,f2=f(p2)-s, ! f3=f(p3)-s) are used to calculate the new value of p such that r(p)=s. ! convergence is guaranteed by taking f1 > 0 and f3 < 0. ! ********************************************************************************************** ! initial value for p. p1 = zero f1 = fp0-s p3 = -one f3 = fpms p = one check1 = .false. check3 = .false. ! iteration process to find the root of f(p)=s. root_iterations: do iter = 1,maxit ! find the smoothing spline sp(u,v) and the corresponding sum of ! squared residuals fp. call fpgrpa(ifsu,ifsv,ifbu,ifbv,idim,ipar,u,mu,v,mv,z,mz,tu, & nu,tv,nv,p,c,nc,fp,fpintu,fpintv,mm,mvnu,wrk(lsu),wrk(lsv), & wrk(lri),wrk(lq),wrk(lau),wrk(lau1),wrk(lav),wrk(lav1), & wrk(lbu),wrk(lbv),nru,nrv) ! test whether the approximation sp(u,v) is an acceptable solution. fpms = fp-s; if (abs(fpms)<acc) return ! find the new value of p and carry out one more step. call root_finding_iterate(p1,f1,p2,f2,p3,f3,p,fpms,acc,check1,check3,success) if (.not.success) then ier = FITPACK_S_TOO_SMALL return end if end do root_iterations ! Maximum number of iterations reached. ier = FITPACK_MAXIT return end subroutine fppasu ! Periodic spline determination pure subroutine fpperi(iopt,x,y,w,m,k,s,nest,tol,maxit, & k1,k2,n,t,c,fp,fpint,z,a1,a2,b,g1,g2,q,nrdata,ier) ! ..scalar arguments.. real(RKIND), intent(in) :: s,tol real(RKIND), intent(inout) :: fp integer, intent(in) :: iopt,m,k,k1,k2,nest,maxit integer, intent(inout) :: n,ier ! ..array arguments.. real(RKIND), intent(in) :: x(m),y(m),w(m) real(RKIND), intent(inout) :: t(nest),c(nest),fpint(nest),z(nest),a1(nest,k1),a2(nest,k), & b(nest,k2),g1(nest,k2),g2(nest,k1),q(m,k1) integer, intent(inout) :: nrdata(nest) ! ..local scalars.. real(RKIND) :: acc,cos,c1,d1,fpart,fpms,fpold,fp0,f1,f2,f3,p,per,pinv,piv,p1,p2,p3,sin,store,& term,wi,xi,yi,rn integer :: i,ij,ik,it,iter,i1,i2,i3,j,jk,jper,j1,j2,kk,kk1,k3,l,l0,l1,l5,mm,m1,new,& nk1,nk2,nmax,nmin,nplus,npl1,nrint,n10,n11,n7,n8 ! ..local arrays.. real(RKIND) :: h(MAX_ORDER+1),h1(7),h2(6) logical :: done,check1,check3,success fpold = zero fp0 = zero nplus = 0 ! ***** ! part 1: determination of the number of knots and their position ! ***** ! given a set of knots we compute the least-squares periodic spline sinf(x). if the ! sum f(p=inf) <= s we accept the choice of knots. the initial choice of knots depends on the ! value of s and iopt. ! if s=0 we have spline interpolation; in that case the number of knots equals nmax = m+2*k. c ! if s > 0 and iopt=0 iopt=0 we first compute the least-squares polynomial of degree k; ! n = nmin = 2*k+2. since s(x) must be periodic we find that s(x) is a constant function. c ! iopt=1 we start with the set of knots found at the last call of the routine, except for ! the case that s > fp0; then we compute directly the least-squares periodic polynomial. ! ***** m1 = m-1 kk = k kk1 = k1 k3 = 3*k+1 nmin = 2*k1 ier = FITPACK_OK ! determine the length of the period of s(x). per = x(m)-x(1) if (iopt>=0) then ! calculation of acc, the absolute tolerance for the root of f(p)=s. acc = tol*s ! determine nmax, the number of knots for periodic spline interpolation nmax = m+2*k interp_or_fit: if (s<=zero .and. nmax/=nmin) then ! if s=0, s(x) is an interpolating spline. n = nmax ! test whether the required storage space exceeds the available one. if (n>nest) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! find the position of the interior knots in case of interpolation. call fpperi_reset_interp(k,m,n,nest,kk,kk1,x,y,t,c,fp,per,fp0,s,fpint,nrdata,done) if (done) then ier = FITPACK_INTERPOLATING_OK return end if else interp_or_fit ! if s > 0 our initial choice depends on the value of iopt. ! if iopt=0 or iopt=1 and s>=fp0, we start computing the least-squares ! periodic polynomial. (i.e. a constant function). ! if iopt=1 and fp0>s we start computing the least-squares periodic ! spline according the set of knots found at the last call of the routine. if (iopt/=0 .and. n/=nmin) then fp0 = fpint(n) fpold = fpint(n-1) nplus = nrdata(n) endif ! the case that s(x) is a constant function is treated separetely. ! find the least-squares constant c1 and compute fp0 at the same time. if (iopt==0 .or. n==nmin .or. (iopt/=0 .and. n/=nmin .and. s>=fp0)) then fp0 = zero d1 = zero c1 = zero do it=1,m1 wi = w(it) yi = y(it)*wi call fpgivs(wi,d1,cos,sin) call fprota(cos,sin,yi,c1) fp0 = fp0+yi**2 end do c1 = c1/d1 ! test whether that constant function is a solution of our problem. fpms = fp0-s is_constant: if (fpms<acc .or. nmax==nmin) then ier = FITPACK_LEASTSQUARES_OK ! the least-squares constant function c1 is a solution of our problem. A constant ! function is a spline of degree k with all b-spline coefficients equal to c1. do i=1,k1 rn = k1-i t(i) = x(1)-rn*per c(i) = c1 j = i+k1 rn = i-1 t(j) = x(m)+rn*per end do n = nmin fp = fp0 fpint(n-1:n) = [zero,fp0] nrdata(n) = 0 return endif is_constant fpold = fp0 ! test whether the required storage space exceeds the available one. if (nmin>=nest) then ier = FITPACK_INSUFFICIENT_STORAGE return endif ! start computing the least-squares periodic spline with one interior knot. nplus = 1 n = nmin+1 mm = (m+1)/2 t(k2) = x(mm) nrdata(1) = mm-2 nrdata(2) = m1-mm endif endif interp_or_fit endif ! main loop for the different sets of knots. m is a save upper bound for the number of trials. iter = 0 update_knots: do while (iter<m) iter = iter+1 ! find nrint, the number of knot intervals. nrint = n-nmin+1 ! find the position of the additional knots which are needed for the b-spline representation ! of s(x). if we take ! t(k+1) = x(1), t(n-k) = x(m) ! t(k+1-j) = t(n-k-j) - per, j=1,2,...k ! t(n-k+j) = t(k+1+j) + per, j=1,2,...k ! then s(x) is a periodic spline with period per if the b-spline coefficients satisfy the ! following conditions ! c(n7+j) = c(j), j=1,...k (**) with n7=n-2*k-1. t(k1) = x(1) nk1 = n-k1 nk2 = nk1+1 t(nk2) = x(m) do j=1,k i1 = nk2+j i2 = nk2-j j1 = k1+j j2 = k1-j t(i1) = t(j1)+per t(j2) = t(i2)-per end do ! compute the b-spline coefficients c(j),j=1,...n7 of the least-squares periodic spline ! sinf(x). the observation matrix a is built up row by row while taking into account ! condition (**) and is reduced to triangular form by givens transformations . ! at the same time fp=f(p=inf) is computed. ! the n7 x n7 triangularised upper matrix a has the form ! ! a1 ' ! ! a = ! ' a2 ! ! ! 0 ' ! ! with a2 a n7xk matrix and a1 a n10xn10 upper triangular matrix of bandwidth k+1 (n10=n7-k). ! initialization. z (1:nk1) = zero a1(1:nk1,1:kk1) = zero n7 = nk1-k n10 = n7-kk jper = 0 fp = zero l = k1 get_coefs: do it=1,m1 ! fetch the current data point x(it),y(it) xi = x(it) wi = w(it) yi = y(it)*wi ! search for knot interval t(l) <= xi < t(l+1). do while (xi>=t(l+1)) l = l+1 end do ! evaluate the (k+1) non-zero b-splines at xi and store them in q. h = fpbspl(t,n,k,xi,l) q(it,:k1) = h(:k1) h(:k1) = h(:k1)*wi l5 = l-k1 ! test whether the b-splines nj,k+1(x),j=1+n7,...nk1 are all zero at xi if (l5>=n10) then if (jper==0) then ! initialize the matrix a2. a2(1:n7,1:kk) = zero jk = n10+1 do i=1,kk ik = jk do j=1,kk1 if (ik<=0) exit a2(ik,i) = a1(ik,j) ik = ik-1 end do jk = jk+1 end do jper = 1 endif ! if one of the b-splines nj,k+1(x),j=n7+1,...nk1 is not zero at xi ! we take account of condition (**) for setting up the new row ! of the observation matrix a. this row is stored in the arrays h1 ! (the part with respect to a1) and h2 (the part with respect to a2). h1 = zero h2 = zero j = l5-n10 do i=1,kk1 j = j+1 l0 = j l1 = l0-kk do while (l1>max(0,n10)) l0 = l1-n10 l1 = l0-kk end do if (l1>0) then h1(l1) = h(i) else h2(l0) = h2(l0)+h(i) end if end do ! rotate the new row of the observation matrix into triangle ! by givens transformations. if (n10>0) then ! rotation with the rows 1,2,...n10 of matrix a. one_to_n10: do j=1,n10 piv = h1(1) if (equal(piv,zero)) then h1(1:kk1) = [h1(2:kk1),zero] else ! calculate the parameters of the givens transformation. call fpgivs(piv,a1(j,1),cos,sin) ! transformation to the right hand side. call fprota(cos,sin,yi,z(j)) ! transformations to the left hand side with respect to a2. call fprota(cos,sin,h2(:kk),a2(j,:kk)) if (j==n10) exit one_to_n10 i2 = min(n10-j,kk)+1 ! transformations to the left hand side with respect to a1. call fprota(cos,sin,h1(2:i2),a1(j,2:i2)) h1(1:i2) = [h1(2:i2),zero] endif end do one_to_n10 endif ! rotation with the rows n10+1,...n7 of matrix a. n10_to_n7: do j=1,kk ij = n10+j piv = h2(j) if (ij<=0 .or. equal(piv,zero)) cycle n10_to_n7 ! calculate the parameters of the givens transformation. call fpgivs(piv,a2(ij,j),cos,sin) ! transformations to right hand side. call fprota(cos,sin,yi,z(ij)) if (j==kk) exit n10_to_n7 ! transformations to left hand side. j1 = j+1 call fprota(cos,sin,h2(j1:kk),a2(ij,j1:kk)) end do n10_to_n7 else ! l5<n10 ! rotation of the new row of the observation matrix into triangle in case the b-splines ! nj,k+1(x),j=n7+1,...n-k-1 are all zero at xi. j = l5 new_row: do i=1,kk1 j = j+1 piv = h(i) if (equal(piv,zero)) cycle new_row call fpgivs(piv,a1(j,1),cos,sin) ! calculate the parameters of the givens transformation. ! transformations to right hand side. call fprota(cos,sin,yi,z(j)) if (i==kk1) exit new_row i2 = 1 i3 = i+1 ! transformations to left hand side. TODO replace with array call do i1=i3,kk1 i2 = i2+1 call fprota(cos,sin,h(i1),a1(j,i2)) end do end do new_row endif ! add contribution of this row to the sum of squares of residual rhs fp = fp+yi**2 end do get_coefs fpint(n-1:n) = [fpold,fp0] nrdata(n) = nplus ! backward substitution to obtain the b-spline coefficients c(j),j=1,.n c(1:n7) = fpbacp(a1,a2,z,n7,kk,kk1,nest) ! calculate from condition (**) the coefficients c(j+n7),j=1,2,...k. c(n7+1:n7+k) = c(1:k) ! Success! if the approximation sinf(x) is an acceptable solution. fpms = fp-s if (iopt<0 .or. abs(fpms)<acc) return ! if f(p=inf) < s accept the choice of knots. if (fpms<zero) exit update_knots ! if n=nmax, sinf(x) is an interpolating spline. if (n==nmax) then ier = FITPACK_INTERPOLATING_OK return end if ! increase the number of knots. ! if n=nest we cannot increase the number of knots because of the ! storage capacity limitation. if (n==nest) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! determine the number of knots nplus we are going to add. npl1 = merge(int((nplus*fpms)/(fpold-fp)),nplus*2,fpold-fp>acc) nplus = min(nplus*2,max(npl1,nplus/2,1)) fpold = fp ! compute the sum(wi*(yi-s(xi))**2) for each knot interval ! t(j+k) <= xi <= t(j+k+1) and store it in fpint(j),j=1,2,...nrint. fpart = zero i = 1 l = k1 do it=1,m1 if (x(it)>=t(l)) then new = 1 l = l+1 endif term = zero l0 = l-k2 do j=1,k1 l0 = l0+1 term = term+c(l0)*q(it,j) end do term = (w(it)*(term-y(it)))**2 fpart = fpart+term if (new/=0) then if (l>k2) then store = term*half fpint(i) = fpart-store i = i+1 fpart = store else fpint(nrint) = term endif new = 0 endif end do fpint(nrint) = fpint(nrint)+fpart add_new_knots: do l=1,nplus ! add a new knot call fpknot(x,m,t,n,fpint,nrdata,nrint,nest,1) ! if n=nmax we locate the knots as for interpolation. if (n==nmax) then ! find the position of the interior knots in case of interpolation. call fpperi_reset_interp(k,m,n,nest,kk,kk1,x,y,t,c,fp,per,fp0,s,fpint,nrdata,done) if (done) then ier = FITPACK_INTERPOLATING_OK return else ! Restart iteration iter = 0 cycle update_knots end if end if ! test whether we cannot further increase the number of knots. if (n==nest) exit add_new_knots end do add_new_knots ! restart the computations with the new set of knots. end do update_knots ! ***** ! part 2: determination of the smoothing periodic spline sp(x). ! ***** ! we have determined the number of knots and their position. ! we now compute the b-spline coefficients of the smoothing spline sp(x). the observation ! matrix a is extended by the rows of matrix b expressing that the kth derivative discontinuities ! of sp(x) at the interior knots t(k+2),...t(n-k-1) must be zero. the corresponding weights of ! these additional rows are set to 1/sqrt(p). iteratively we then have to determine the value of p ! such that f(p)=sum(w(i)*(y(i)-sp(x(i)))**2) be = s. we already know that the least-squares ! constant function corresponds to p=0, and that the least-squares periodic spline corresponds to ! p=infinity. the iteration process which is proposed here, makes use of rational interpolation. ! since f(p) is a convex and strictly decreasing function of p, it can be approximated by a rational ! function r(p) = (u*p+v)/(p+w). three values of p(p1,p2,p3) with corresponding values of f(p) ! (f1=f(p1)-s,f2=f(p2)-s,f3=f(p3)-s) are used to calculate the new value of p such that r(p)=s. ! convergence is guaranteed by taking f1>0 and f3<zero ! ***** ! evaluate the discontinuity jump of the kth derivative of the b-splines at the knots ! t(l),l=k+2,...n-k-1 and store in b. call fpdisc(t,n,k2,b,nest) ! initial value for p. p = zero p1 = zero f1 = fp0-s p3 = -one f3 = fpms n11 = n10-1 n8 = n7-1 l = n7 do i=1,k j = k+1-i p = p+a2(l,j) l = l-1 if (l==0) exit end do if (l/=0) p = p+sum(a1(1:n10,1)) rn = n7 p = rn/p check1 = .false. check3 = .false. ! iteration process to find the root of f(p) = s. find_root: do iter=1,maxit ! form the matrix g as the matrix a extended by the rows of matrix b. ! the rows of matrix b with weight 1/p are rotated into ! the triangularised observation matrix a. ! after triangularisation our n7 x n7 matrix g takes the form ! ! g1 ' ! ! g = ! ' g2 ! ! ! 0 ' ! ! with g2 a n7 x (k+1) matrix and g1 a n11 x n11 upper triangular ! matrix of bandwidth k+2. ( n11 = n7-k-1) pinv = one/p ! store matrix a into g c (1:n7) = z(1:n7) g1(1:n7,1:k1) = a1(1:n7,1:k1) g1(1:n7,k2) = zero g2(1:n7,1) = zero g2(1:n7,2:k1) = a2(1:n7,1:k) l = n10 do j=1,k1 if (l<=0) exit g2(l,1) = a1(l,j) l = l-1 end do n8_rows: do it=1,n8 ! fetch a new row of matrix b and store it in the arrays h1 (the part ! with respect to g1) and h2 (the part with respect to g2). yi = zero h1(:k2) = zero h2(:k1) = zero if (it<=n11) then l = it l0 = it storeh2: do j=1,k2 if (l0==n10) then l0 = 1 do l1=j,k2 h2(l0) = b(it,l1)*pinv l0 = l0+1 end do exit storeh2 endif h1(j) = b(it,j)*pinv l0 = l0+1 end do storeh2 else l = 1 i = it-n10 do j=1,k2 i = i+1 l0 = i l1 = l0-k1 do while (l1>max(0,n11)) l0 = l1-n11 l1 = l0-k1 end do if (l1>0) then h1(l1) = b(it,j)*pinv else h2(l0) = h2(l0)+b(it,j)*pinv end if end do endif ! rotate this row into triangle by givens transformations without square roots. ! rotation with the rows l,l+1,...n11. rot_n11: do j=l,n11 piv = h1(1) ! calculate the parameters of the givens transformation. call fpgivs(piv,g1(j,1),cos,sin) ! transformation to right hand side. call fprota(cos,sin,yi,c(j)) ! transformation to the left hand side with respect to g2. call fprota(cos,sin,h2(1:k1),g2(j,1:k1)) if (j==n11) exit rot_n11 ! transformation to the left hand side with respect to g1. i2 = min(n11-j,k1)+1 call fprota(cos,sin,h1(2:i2),g1(j,2:i2)) h1(1:i2) = [h1(2:i2),zero] end do rot_n11 ! rotation with the rows n11+1,...n7 rot_n10_n7: do j=1,k1 ij = n11+j if (ij<=0) cycle rot_n10_n7 piv = h2(j) ! calculate the parameters of the givens transformation call fpgivs(piv,g2(ij,j),cos,sin) ! transformation to the right hand side. call fprota(cos,sin,yi,c(ij)) if (j==k1) exit rot_n10_n7 ! transformation to the left hand side. j1 = j+1 call fprota(cos,sin,h2(j1:k1),g2(ij,j1:k1)) end do rot_n10_n7 end do n8_rows ! backward substitution to obtain the b-spline coefficients c(j),j=1,2,...n7 of sp(x). c(:n7) = fpbacp(g1,g2,c,n7,k1,k2,nest) ! calculate from condition (**) the b-spline coefficients c(n7+j),j=1,. c(n7+1:n7+k) = c(1:k) ! computation of f(p). fp = zero l = k1 do it=1,m1 if (x(it)>=t(l)) l = l+1 l0 = l-k2 term = dot_product(c(l0+1:l0+k1),q(it,1:k1)) fp = fp+(w(it)*(term-y(it)))**2 end do ! test whether the approximation sp(x) is an acceptable solution. fpms = fp-s if (abs(fpms)<acc) return ! test whether the maximal number of iterations is reached. if (iter==maxit) then ier = FITPACK_MAXIT return end if ! find the new value of p and carry out one more step. call root_finding_iterate(p1,f1,p2,f2,p3,f3,p,fpms,acc,check1,check3,success) if (.not.success) then ier = FITPACK_S_TOO_SMALL return end if end do find_root return end subroutine fpperi pure subroutine fpperi_reset_interp(k,m,n,nest,kk,kk1,x,y,t,c,fp,per,fp0,s,fpint,nrdata,done) integer, intent(in) :: k,m,n,nest integer, intent(inout) :: kk,kk1 real(RKIND), intent(in) :: x(m),y(m),per,fp0,s real(RKIND), intent(inout) :: t(nest),c(nest),fp,fpint(nest) integer, intent(inout) :: nrdata(nest) logical, intent(out) :: done integer :: m1 m1 = m-1 done = .false. k_is_odd: if (mod(k,2)/=0) then t(k+2:k+m1) = x(2:m1) if (s<=zero) then kk = k-1 kk1 = k if (kk<=0) then t(1:2) = [t(m)-per,x(1)] t(m+1:m+2) = [x(m),t(3)+per] c(:m) = [y(1:m1),y(1)] fp = zero fpint(n-1:n) = [zero,fp0] nrdata(n) = 0 done = .true. endif endif else k_is_odd t(k+2:k+m1) = half*(x(2:m1)+x(1:m1-1)) endif k_is_odd end subroutine fpperi_reset_interp ! subroutine fppocu finds a idim-dimensional polynomial curve p(u) = (p1(u),p2(u),...,pidim(u)) of ! degree k, satisfying certain derivative constraints at the end points a and b, i.e. ! (l) ! if ib > 0 : pj (a) = db(idim*l+j), l=0,1,...,ib-1 ! (l) ! if ie > 0 : pj (b) = de(idim*l+j), l=0,1,...,ie-1 ! ! the polynomial curve is returned in its b-spline representation ( cp(j), j=1,2,...,np ) pure subroutine fppocu(idim,k,a,b,ib,db,nb,ie,de,ne,cp,np) ! ..scalar arguments.. integer, intent(in) :: idim,k,ib,nb,ie,ne,np real(RKIND), intent(in) :: a,b ! ..array arguments.. real(RKIND), intent(in) :: db(nb),de(ne) real(RKIND), intent(inout) :: cp(np) ! ..local scalars.. real(RKIND) :: ab,aki integer :: i,id,j,jj,l,ll,k1,k2 ! ..local array.. real(RKIND) :: work(6,6) ! .. k1 = k+1 k2 = 2*k1 ab = b-a all_dims: do id=1,idim ! Reset temporary storage work(:k1,1) = zero if (ib>0) then work(1,1:ib) = db(id:id+(ib-1)*idim:idim) if (ib>1) then ! probably unnecessary ll = ib do j=2,ib ll = ll-1 do i=1,ll aki = k1-i work(j,i) = ab*work(j-1,i+1)/aki + work(j-1,i) end do end do endif endif if (ie>0) then l = id j = k1 do i=1,ie work(j,i) = de(l) l = l+idim j = j-1 end do if (ie>1) then ! probably unnecessary ll = ie do jj=2,ie ll = ll-1 j = k1+1-jj do i=1,ll aki = k1-i work(j,i) = work(j+1,i) - ab*work(j,i+1)/aki j = j-1 end do end do endif endif l = (id-1)*k2 cp(l+1:l+k1) = work(:k1,1) end do all_dims return end subroutine fppocu pure subroutine fppogr(iopt,ider,u,mu,v,mv,z,mz,z0,r,s, & nuest,nvest,tol,maxit,nc,nu,tu,nv,tv,c,fp,fp0,fpold,reducu, & reducv,fpintu,fpintv,dz,step,lastdi,nplusu,nplusv,lasttu,nru, & nrv,nrdatu,nrdatv,wrk,lwrk,ier) ! .. ! ..scalar arguments.. integer, intent(in) :: mu,mv,mz,nuest,nvest,nc,lwrk,maxit integer, intent(inout) :: nu,nv,lastdi,nplusu,nplusv,lasttu,ier real(RKIND), intent(in) :: z0,r,s,tol real(RKIND), intent(inout) :: fp,fp0,fpold,step,reducu,reducv ! ..array arguments.. integer, intent(in) :: iopt(3),ider(2) integer, intent(inout) :: nrdatu(nuest),nrdatv(nvest),nru(mu),nrv(mv) real(RKIND), intent(in) :: u(mu),v(mv),z(mz) real(RKIND), intent(inout) :: c(nc),tu(nuest),tv(nvest),dz(3),wrk(lwrk),fpintu(nuest),fpintv(nvest) ! ..local scalars.. real(RKIND) :: acc,fpms,f1,f2,f3,p,p1,p2,p3,vb,ve,zmax,zmin,rn integer :: i,ifbu,ifbv,ifsu,ifsv,istart,iter,i1,i2,j,ju,ktu,l,mpm,mumin,mu0,mu1,& nn,nplu,nplv,npl1,nrintu,nrintv,nue,numax,nve,nvmax logical :: check1,check3,success ! ..local arrays.. integer :: idd(2) real(RKIND) :: dzz(3) ! set constants real(RKIND), parameter :: period = 2*pi ! initialization nplu = 0 nplv = 0 ifsu = 0 ifsv = 0 ifbu = 0 ifbv = 0 p = -one mumin = 4-iopt(3) - merge(1,0,ider(1)>=0) - merge(1,0,iopt(2)==1 .and. ider(2)==1) vb = v(1) ve = vb+period ! ***************************************************************************************************** ! part 1: determination of the number of knots and their position. ! ***************************************************************************************************** ! given a set of knots we compute the least-squares spline sinf(u,v) and the corresponding sum of ! squared residuals fp = f(p=inf). ! if iopt(1)=-1 sinf(u,v) is the requested approximation. ! if iopt(1)>=0 we check whether we can accept the knots: ! if fp <= s we will continue with the current set of knots. ! if fp > s we will increase the number of knots and compute the corresponding least-squares spline ! until finally fp <= s. the initial choice of knots depends on the value of s and iopt. ! if s=0 we have spline interpolation; in that case the number of knots in the u-direction equals ! nu=numax=mu+5+iopt(2)+iopt(3) and in the v-direction nv=nvmax=mv+7. ! if s>0 and: ! iopt(1)=0 we first compute the least-squares polynomial,i.e. a spline without interior knots: ! nu=8 ; nv=8. ! iopt(1)=1 we start with the set of knots found at the last call of the routine, except for the ! case that s > fp0; then we compute the least-squares polynomial directly. ! ***************************************************************************************************** if (iopt(1)>=0) then ! acc denotes the absolute tolerance for the root of f(p)=s. acc = tol*s ! numax and nvmax denote the number of knots needed for interpolation. numax = mu+5+iopt(2)+iopt(3) nvmax = mv+7 nue = min(numax,nuest) nve = min(nvmax,nvest) if (s<=zero) then ! if s = 0, s(u,v) is an interpolating spline. nu = numax nv = nvmax ! test whether the required storage space exceeds the available one. if (nu>nuest .or. nv>nvest) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! find the position of the knots in the v-direction. tv(1:mv+7) = [v(mv-2:mv)-period,v(1:mv),ve,v(2:4)+period] ! if not all the derivative values g(i,j) are given, we will first ! estimate these values by computing a least-squares spline idd(1) = merge(ider(1),1,ider(1)/=0) idd(2) = ider(2) if (idd(1)>0) dz(1) = z0 if (ider(1)<0 .or. (iopt(2)/=0 .and. ider(2)==0)) then ! we set up the knots in the u-direction for computing the least-squares spline. i1 = 3 i2 = mu-2 nu = 4 do i=1,mu if (i1>i2) exit nu = nu+1 tu(nu) = u(i1) i1 = i1+2 end do do i=1,4 tu(i) = zero nu = nu+1 tu(nu) = r end do ! we compute the least-squares spline for estimating the derivatives. call fpopdi(ifsu,ifsv,ifbu,ifbv,u,mu,v,mv,z,mz,z0,dz,iopt,idd, & tu,nu,tv,nv,nuest,nvest,p,step,c,nc,fp,fpintu,fpintv,nru,nrv, & wrk,lwrk) ifsu = 0 endif ! if all the derivatives at the origin are known, we compute the interpolating spline. ! we set up the knots in the u-direction, needed for interpolation. nn = numax-8 if (nn/=0) then ju = 2-iopt(2) do l=1,nn tu(l+4) = u(ju) ju = ju+1 end do nu = numax l = nu do i=1,4 tu(i) = zero tu(l) = r l = l-1 end do endif ! we compute the interpolating spline. call fpopdi(ifsu,ifsv,ifbu,ifbv,u,mu,v,mv,z,mz,z0,dz,iopt,idd, & tu,nu,tv,nv,nuest,nvest,p,step,c,nc,fp,fpintu,fpintv,nru,nrv, & wrk,lwrk) ier = FITPACK_INTERPOLATING_OK fp = zero return else ! if s>0 our initial choice of knots depends on the value of iopt(1). ier = FITPACK_OK if (iopt(1)/=0) step = -step if (iopt(1)/=0 .and. fp0>s) then ! if iopt(1)=1 and fp0 > s we start computing the least-squares spline ! according to the set of knots found at the last call of the routine. ! we determine the number of grid coordinates u(i) inside each knot ! interval (tu(l),tu(l+1)). l = 5 j = 1 nrdatu(1) = 0 mu0 = 2-iopt(2) mu1 = mu-2+iopt(3) do i=mu0,mu1 nrdatu(j) = nrdatu(j)+1 if (u(i)>=tu(l)) then nrdatu(j) = nrdatu(j)-1 l = l+1 j = j+1 nrdatu(j) = 0 endif end do ! we determine the number of grid coordinates v(i) inside each knot ! interval (tv(l),tv(l+1)). l = 5 j = 1 nrdatv(1) = 0 do i=2,mv nrdatv(j) = nrdatv(j)+1 if (v(i)>=tv(l)) then nrdatv(j) = nrdatv(j)-1 l = l+1 j = j+1 nrdatv(j) = 0 endif end do idd = ider else ! if iopt(1)=0 or iopt(1)=1 and s >= fp0,we start computing the least- ! squares polynomial (which is a spline without interior knots). ier = FITPACK_LEASTSQUARES_OK idd = [ider(1),1] nu = 8 nv = 8 nrdatu(1) = mu-3+iopt(2)+iopt(3) nrdatv(1) = mv-1 lastdi = KNOT_DIM_NONE nplusu = 0 nplusv = 0 fp0 = zero fpold = zero reducu = zero reducv = zero endif endif endif ! main loop for the different sets of knots. ! mpm=mu+mv is a safe upper bound for the number of trials. mpm = mu+mv iterations: do iter=1,mpm ! number of knot intervals in the u (v) directions. nrintu = nu-7 nrintv = nv-7 ! position of the additional knots which are needed for the b-spline representation of s(u,v). tu(1:4) = zero tu(nu-3:nu) = r tv(1:4) = [tv(nv-6:nv-4)-period,vb] tv(nv-3:nv) = [ve,tv(5:7)+period] ! find an estimate of the range of possible values for the optimal derivatives at the origin. ktu = max(mumin,merge(mu,nrdatu(1)+2-iopt(2),nrintu==1)) if (ktu/=lasttu) then l = mv*ktu zmin = min(z0,minval(z(:l),1)) zmax = max(z0,maxval(z(:l),1)) step = zmax-zmin lasttu = ktu endif ! find the least-squares spline sinf(u,v). call fpopdi(ifsu,ifsv,ifbu,ifbv,u,mu,v,mv,z,mz,z0,dz,iopt,idd, & tu,nu,tv,nv,nuest,nvest,p,step,c,nc,fp,fpintu,fpintv,nru,nrv, & wrk,lwrk) step = abs(step) if (ier==FITPACK_LEASTSQUARES_OK) fp0 = fp ! test whether the least-squares spline is an acceptable solution. fpms = fp-s if (iopt(1)<0 .or. abs(fpms)<acc) return ! if f(p=inf) < s, we accept the choice of knots. if (fpms<zero) exit iterations ! if nu=numax and nv=nvmax, sinf(u,v) is an interpolating spline if (nu==numax .and. nv==nvmax) then fp = zero ier = FITPACK_INTERPOLATING_OK return end if ! *** increase the number of knots. *** ! if nu=nue and nv=nve we cannot further increase the number of knots ! because of the storage capacity limitation. if (nu==nue .and. nv==nve) then ier = FITPACK_INSUFFICIENT_STORAGE return endif if (ider(1)==0) fpintu(1) = fpintu(1)+(z0-c(1))**2 ier = FITPACK_OK ! adjust the parameter reducu or reducv according to the direction ! in which the last added knots were located. first_knot: if (lastdi==KNOT_DIM_NONE) then nplv = 3 idd(2) = ider(2) fpold = fp else first_knot select case (lastdi) case (KNOT_DIM_1); reducu = fpold-fp case (KNOT_DIM_2); reducv = fpold-fp end select ! store the sum of squared residuals for the current set of knots. fpold = fp ! find nplu, the number of knots we should add in the u-direction. nplu = 1 if (nu/=8) then npl1 = nplusu*2 rn = nplusu if (reducu>acc) npl1 = int(rn*fpms/reducu) nplu = min(nplusu*2,max(npl1,nplusu/2,1)) endif ! find nplv, the number of knots we should add in the v-direction. nplv = 3 if (nv/=8) then npl1 = nplusv*2 rn = nplusv if (reducv>acc) npl1 = int(rn*fpms/reducv) nplv = min(nplusv*2,max(npl1,nplusv/2,1)) endif endif first_knot ! test whether we are going to add knots in the u- or v-direction. lastdi = new_knot_dimension(nu,nplu,nue,nv,nplv,nve,lastdi) choose_dir: if (lastdi==KNOT_DIM_2) then ! addition in the v-direction. nplusv = nplv ifsv = 0 add_v_knots: do l=1,nplusv ! add a new knot in the v-direction. call fpknot(v,mv,tv,nv,fpintv,nrdatv,nrintv,nvest,1) ! test whether we cannot further increase the number of knots in the v-direction. if (nv==nve) exit add_v_knots end do add_v_knots else choose_dir ! addition in the u-direction. nplusu = nplu ifsu = 0 istart = merge(1,0,iopt(2)==0) add_u_knots: do l=1,nplusu ! add a new knot in the u-direction call fpknot(u,mu,tu,nu,fpintu,nrdatu,nrintu,nuest,istart) ! test whether we cannot further increase the number of knots in the u-direction. if (nu==nue) exit add_u_knots end do add_u_knots endif choose_dir ! restart the computations with the new set of knots. end do iterations ! test whether the least-squares polynomial is a solution of our approximation problem. if (ier==FITPACK_LEASTSQUARES_OK) return ! ********************************************************************************************** ! part 2: determination of the smoothing spline sp(u,v) ! ********************************************************************************************** ! we have determined the number of knots and their position. we now compute the b-spline ! coefficients of the smoothing spline sp(u,v). this smoothing spline depends on the parameter ! p in such a way that ! f(p) = sumi=1,mu(sumj=1,mv((z(i,j)-sp(u(i),v(j)))**2) ! is a continuous, strictly decreasing function of p. moreover the least-squares polynomial ! corresponds to p=0 and the least-squares spline to p=infinity. then iteratively we have to ! determine the positive value of p such that f(p)=s. the process which is proposed here makes ! use of rational interpolation. f(p) is approximated by a rational function r(p)=(u*p+v)/(p+w); ! three values of p (p1,p2,p3) with corresponding values of f(p) (f1=f(p1)-s,f2=f(p2)-s, ! f3=f(p3)-s) are used to calculate the new value of p such that r(p)=s. ! convergence is guaranteed by taking f1 > 0 and f3 < 0. ! ********************************************************************************************** ! initial value for p. p1 = zero f1 = fp0-s p3 = -one f3 = fpms p = one dzz(1:3) = dz(1:3) check1 = .false. check3 = .false. ! iteration process to find the root of f(p)=s. root_iterations: do iter = 1,maxit ! find the smoothing spline sp(u,v) and the corresponding sum f(p). call fpopdi(ifsu,ifsv,ifbu,ifbv,u,mu,v,mv,z,mz,z0,dzz,iopt,idd, & tu,nu,tv,nv,nuest,nvest,p,step,c,nc,fp,fpintu,fpintv,nru,nrv, & wrk,lwrk) ! SUCCESS! the approximation sp(u,v) is an acceptable solution. fpms = fp-s; if (abs(fpms)<acc) return ! find the new value of p and carry out one more step. call root_finding_iterate(p1,f1,p2,f2,p3,f3,p,fpms,acc,check1,check3,success) if (.not.success) then ier = FITPACK_S_TOO_SMALL return end if end do root_iterations ! Maximum number of iterations reached. ier = FITPACK_MAXIT return end subroutine fppogr pure subroutine fppola(iopt1,iopt2,iopt3,m,u,v,z,w,rad,s, nuest,nvest,eta,tol,maxit, & ib1,ib3,nc,ncc,intest,nrest,nu,tu,nv,tv,c,fp,sup,fpint,coord, & f,ff,row,cs,cosi,a,q,bu,bv,spu,spv,h,index,nummer,wrk,lwrk,ier) ! ..scalar arguments.. integer , intent(in) :: iopt1,iopt2,iopt3,m,nuest,nvest,maxit,ib1,ib3,nc,ncc,intest,nrest,lwrk integer , intent(inout) :: ier,nu,nv real(RKIND), intent(in) :: s,eta,tol real(RKIND), intent(inout) :: fp,sup ! ..array arguments.. integer , intent(inout) :: index(nrest),nummer(m) real(RKIND), intent(in) :: u(m),v(m),z(m),w(m) real(RKIND), intent(inout) :: tu(nuest),tv(nvest),c(nc),fpint(intest),coord(intest),f(ncc),ff(nc),row(nvest), & cs(nvest),cosi(5,nvest),a(ncc,ib1),q(ncc,ib3),bu(nuest,5),bv(nvest,5),spu(m,4), & spv(m,4),h(ib3),wrk(lwrk) ! ..user supplied function.. procedure(boundary) :: rad ! ..local scalars.. real(RKIND) :: acc,arg,co,c1,c2,c3,c4,dmax,eps,fac,fac1,fac2,fpmax,fpms,f1,f2,f3,huj,p,pinv,piv,p1,p2,p3, & r,ratio,si,sigma,sq,store,uu,u2,u3,wi,zi,rn integer :: i,iband,iband3,iband4,ii,il,in,ipar,ipar1,irot,iter,i1,i2,j,jrot,j1,j2,l,la,lf,lh,ll,& lu,lv,lwest,l1,l2,l3,l4,ncof,ncoff,nvv,nv4,nreg,nrint,nrr,nr1,nuu,nu4,num,num1,numin,nvmin,rank,& iband1,jlu logical :: check1,check3,success ! ..local arrays.. real(RKIND), dimension(MAX_ORDER+1) :: hu,hv fpms = zero ipar = iopt2*(iopt2+3)/2 ipar1 = ipar+1 eps = sqrt(eta) iband1 = 0 initialize: if (iopt1>=0) then ! calculation of acc, the absolute tolerance for the root of f(p)=s. acc = tol*s numin = 9 nvmin = 9+iopt2*(iopt2+1) if (iopt1==0 .or. s>=sup) then ! if iopt1 = 0 we begin by computing the weighted least-squares polymomial of the form ! s(u,v) = f(1)*(1-u**3)+f(2)*u**3+f(3)*(u**2-u**3)+f(4)*(u-u**3) ! where f(4) = 0 if iopt2> 0 , f(3) = 0 if iopt2 > 1 and ! f(2) = 0 if iopt3> 0. ! the corresponding weighted sum of squared residuals gives the upper ! bound sup for the smoothing factor s. sup = zero f(1:4) = zero a(1:4,1:4) = zero initial_poly: do i=1,m wi = w(i) zi = z(i)*wi uu = u(i) u2 = uu*uu u3 = uu*u2 h(1) = (one-u3)*wi h(2) = merge(u3*wi,zero,iopt3==0) h(3) = merge(u2*(one-uu)*wi,zero,iopt2<=1) h(4) = merge(uu*(one-u2)*wi,zero,iopt2<=0) do j=1,4 piv = h(j) if (equal(piv,zero)) cycle call fpgivs(piv,a(j,1),co,si) call fprota(co,si,zi,f(j)) if (j<4) then j1 = j+1 j2 = 1 do l=j1,4 j2 = j2+1 call fprota(co,si,h(l),a(j,j2)) end do endif end do sup = sup+zi*zi end do initial_poly if (not_equal(a(4,1),zero)) f(4) = f(4)/a(4,1) if (not_equal(a(3,1),zero)) f(3) = (f(3)-a(3,2)*f(4))/a(3,1) if (not_equal(a(2,1),zero)) f(2) = (f(2)-a(2,2)*f(3)-a(2,3)*f(4))/a(2,1) if (not_equal(a(1,1),zero)) f(1) = (f(1)-a(1,2)*f(2)-a(1,3)*f(3)-a(1,4)*f(4))/a(1,1) ! find the b-spline representation of this least-squares polynomial c1 = f(1) c4 = f(2) c2 = f(4)/three+c1 c3 = (f(3)+two*f(4))/three+c1 nu = 8 nv = 8 do i=1,4 c(i) = c1 c(i+4) = c2 c(i+8) = c3 c(i+12) = c4 tu(i) = zero tu(i+4) = one rn = 2*i-9 tv(i) = rn*pi rn = 2*i-1 tv(i+4) = rn*pi end do fp = sup ! test whether the least-squares polynomial is an acceptable solution fpms = sup-s if (fpms<acc) then ier = FITPACK_LEASTSQUARES_OK return end if endif ! Find the initial set of interior knots initial_knots: if (iopt1==0 .or. (iopt1>0 .and. (s>=sup .or. (s<sup .and. nv<nvmin)))) then ! test whether we cannot further increase the number of knots. if (nuest<numin .or. nvest<nvmin) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! find the initial set of interior knots of the spline in case iopt1=0. nu = numin nv = nvmin tu(5) = half nvv = nv-8 rn = nvv+1 fac = pi2/rn forall (i=1:nvv) tv(i+4) = i*fac-pi endif initial_knots endif initialize ! ************************************************************************************************************ ! part 1 : computation of least-squares bicubic splines. ! ************************************************************************************************************ ! if iopt1<0 we compute the least-squares bicubic spline according to the given set of knots. ! if iopt1>=0 we compute least-squares bicubic splines with increasing numbers of knots until the ! corresponding sum f(p=inf)<=s. ! the initial set of knots then depends on the value of iopt1: ! if iopt1=0 we start with one interior knot in the u-direction (0.5) and 1+iopt2*(iopt2+1) in the ! v-direction. ! if iopt1>0 we start with the set of knots found at the last call of the routine. ! ************************************************************************************************************ ! main loop for the different sets of knots. m is a save upper bound for the number of trials. compute_knots: do iter=1,m ! find the position of the additional knots which are needed for the ! b-spline representation of s(u,v). l1 = 4 l2 = l1 l3 = nv-3 l4 = l3 tv(l2) = -pi tv(l3) = pi do i=1,3 l1 = l1+1 l2 = l2-1 l3 = l3+1 l4 = l4-1 tv(l2) = tv(l4)-pi2 tv(l3) = tv(l1)+pi2 end do l = nu do i=1,4 tu(i) = zero tu(l) = one l = l-1 end do ! find nrint, the total number of knot intervals and nreg, the number of panels in which the approximation ! domain is subdivided by the intersection of knots. nuu = nu-7 nvv = nv-7 nrr = nvv/2 nr1 = nrr+1 nrint = nuu+nvv nreg = nuu*nvv ! arrange the data points according to the panel they belong to. call fporde(u,v,m,3,3,tu,nu,tv,nv,nummer,index,nreg) if (iopt2/=0) then ! find the b-spline coefficients cosi of the cubic spline approximations for cr(v)=rad(v)*cos(v) ! and sr(v) = rad(v)*sin(v). ! if iopt2=1, and additionally also for cr(v)**2,sr(v)**2 and 2*cr(v)*sr(v) if iopt2=2 a(1:nvv,1:nvv) = zero cosi(1:ipar,1:nvv) = zero ! the coefficients cosi are obtained from interpolation conditions at the knots tv(i),i=4,5,...nv-4. get_coefs: do i=1,nvv l2 = i+3 arg = tv(l2) hv = fpbspl(tv,nv,3,arg,l2) row(1:nvv) = zero ll = i do j=1,3 if (ll>nvv) ll= 1 row(ll) = row(ll)+hv(j) ll = ll+1 end do co = cos(arg) si = sin(arg) r = rad(arg) cs(1:2) = [co,si]*r if (iopt2/=1) cs(3:5) = [cs(:2)**2,cs(1)*cs(2)] do j=1,nvv piv = row(j) if (equal(piv,zero)) cycle call fpgivs(piv,a(j,1),co,si) call fprota(co,si,cs(:ipar),cosi(:ipar,j)) if (j<nvv) call fprota(co,si,row(j1:nvv),a(j,2:nvv-j1+2)) end do end do get_coefs do l=1,ipar cs(1:nvv) = cosi(l,1:nvv) cosi(l,1:nvv) = fpback(a,cs,nvv,nvv,ncc) end do endif ! find ncof, the dimension of the spline and ncoff, the number ! of coefficients in the standard b-spline representation. nu4 = nu-4 nv4 = nv-4 ncoff = nu4*nv4 ncof = ipar1+nvv*(nu4-1-iopt2-iopt3) ! find the bandwidth of the observation matrix a. iband = merge(ncof,4*nvv,nuu-iopt2-iopt3<=1) iband1 = iband-1 ! initialize the observation matrix a. f(1:ncof) = zero a(1:ncof,1:iband) = zero ! initialize the sum of squared residuals. fp = zero ratio = one+tu(6)/tu(5) ! fetch the data points in the new order. main loop for the different panels. panels: do num=1,nreg ! fix certain constants for the current panel; jrot records the column number of the first ! non-zero element in a row of the observation matrix according to a data point of the panel. num1 = num-1 lu = num1/nvv l1 = lu+4 lv = num1-lu*nvv+1 l2 = lv+3 jrot = merge(ipar1+(lu-iopt2-1)*nvv,0,lu>iopt2) lu = lu+1 ! test whether there are still data points in the current panel. in = index(num) points_left: do while (in/=0) ! fetch a new data point. wi = w(in) zi = z(in)*wi ! evaluate for the u-direction, the 4 non-zero b-splines at u(in) hu = fpbspl(tu,nu,3,u(in),l1) ! evaluate for the v-direction, the 4 non-zero b-splines at v(in) hv = fpbspl(tv,nv,3,v(in),l2) ! store the value of these b-splines in spu and spv resp. spu(in,:) = hu(1:4) spv(in,:) = hv(1:4) ! initialize the new row of observation matrix. h(1:iband) = zero ! calculate the non-zero elements of the new row by making the cross ! products of the non-zero b-splines in u- and v-direction and ! by taking into account the conditions of the splines. row(1:nvv) = zero ! take into account the periodicity condition of the bicubic splines. ll = lv do i=1,4 if (ll>nvv) ll=1 row(ll) = row(ll)+hv(i) ll = ll+1 end do ! take into account the other conditions of the splines. if (iopt2/=0 .and. lu<=iopt2+1) & cs(1:ipar) = matmul(cosi(1:ipar,1:nvv),row(1:nvv)) ! fill in the non-zero elements of the new row. j1 = 0 new_row: do j =1,4 jlu = j+lu huj = hu(j) if (jlu>iopt2+2) then if (jlu>nu4 .and. iopt3/=0) cycle new_row h(j1+1:j1+nvv) = row(1:nvv)*huj j1 = j1+nvv else select case (jlu) case (1,2) h(1) = huj j1 = 1 case (3) h(1:3) = [h(1)+huj,huj*cs(1:2)] j1 = 3 case (4) h(1) = h(1)+huj h(2:3) = h(2:3)+huj*ratio*cs(1:2) h(4:6) = huj*cs(3:5) j1 = 6 end select endif end do new_row h(1:iband) = wi*h(1:iband) ! rotate the row into triangle by givens transformations. irot = jrot rotate: do i=1,iband irot = irot+1 piv = h(i) if (equal(piv,zero)) cycle rotate ! calculate the parameters of the givens transformation. call fpgivs(piv,a(irot,1),co,si) ! apply that transformation to the right hand side. call fprota(co,si,zi,f(irot)) ! apply that transformation to the left hand side. if (i<iband) call fprota(co,si,h(i+1:iband),a(irot,2:1+iband-i)) end do rotate ! add the contribution of the row to the sum of squares of residual right hand sides. fp = fp+zi**2 ! find the number of the next data point in the panel. in = nummer(in) end do points_left end do panels ! find dmax, the maximum value for the diagonal elements in the reduced triangle. dmax = max(zero,maxval(a(1:ncof,1))) ! check whether the observation matrix is rank deficient. sigma = eps*dmax if (any(a(1:ncof,1)<=sigma)) then ! in case of rank deficiency, find the minimum norm solution. lwest = ncof*iband+ncof+iband if (lwest>lwrk) then ier = lwest return end if lf = 1 lh = lf+ncof la = lh+iband ff(1:ncof) = f(1:ncof) q(1:ncof,1:iband) = a(1:ncof,1:iband) call fprank(q,ff,ncof,iband,ncc,sigma,c,sq,rank,wrk(la),wrk(lf),wrk(lh)) q(1:ncof,1) = q(1:ncof,1)/dmax ! add to the sum of squared residuals, the contribution of reducing the rank. fp = fp+sq else ! backward substitution in case of full rank. c(:ncof) = fpback(a,f,ncof,iband,ncc) rank = ncof q(1:ncof,1) = a(1:ncof,1)/dmax endif ! find the coefficients in the standard b-spline representation of the spline. call fprppo(nu,nv,iopt2,iopt3,cosi,ratio,c,ff,ncoff) ! test whether the least-squares spline is an acceptable solution. fpms = fp-s if (iopt1<0 .or. abs(fpms)<=acc) then if (fp<=zero) then ier = FITPACK_INTERPOLATING_OK fp = zero return else if (ncof/=rank) ier = -rank return endif endif ! if f(p=inf) < s, accept the choice of knots. if (fpms<zero) exit compute_knots ! test whether we cannot further increase the number of knots if (m<ncof) then ier = FITPACK_TOO_MANY_KNOTS return end if ! search where to add a new knot. ! find for each interval the sum of squared residuals fpint for the data points having the coordinate ! belonging to that knot interval. calculate also coord which is the same sum, weighted by the position ! of the data points considered. fpint(1:nrint) = zero coord(1:nrint) = zero do num=1,nreg num1 = num-1 lu = num1/nvv l1 = lu+1 lv = num1-lu*nvv l2 = lv+1+nuu jrot = lu*nv4+lv in = index(num) do while (in/=0) store = zero i1 = jrot do i=1,4 store = store + spu(in,i)*dot_product(spv(in,1:4),c(i1+1:i1+4)) i1 = i1+nv4 end do store = (w(in)*(z(in)-store))**2 fpint(l1) = fpint(l1)+store coord(l1) = coord(l1)+store*u(in) fpint(l2) = fpint(l2)+store coord(l2) = coord(l2)+store*v(in) in = nummer(in) end do end do ! bring together the information concerning knot panels which are ! symmetric with respect to the origin. do i=1,nrr l1 = nuu+i l2 = l1+nrr fpint(l1) = fpint(l1)+fpint(l2) coord(l1) = coord(l1)+coord(l2)-pi*fpint(l2) end do ! find the interval for which fpint is maximal on the condition that ! there still can be added a knot. l1 = merge(nuu+1, 1,nuest<nu+1) l2 = merge(nuu ,nuu+nrr,nvest<nv+2) ! test whether we cannot further increase the number of knots. if (l1>l2) then ier = FITPACK_INSUFFICIENT_STORAGE return end if add_knot: do ! Set knot at the location with max squared residual fpmax = zero l = 0 do i=l1,l2 if (fpmax<fpint(i)) then l = i fpmax = fpint(i) endif end do ! All residuals already <=0 if (l==0) then ier = FITPACK_OVERLAPPING_KNOTS return end if ! calculate the position of the new knot. arg = coord(l)/fpint(l) ! test in what direction the new knot is going to be added. choose_direction: if (l<=nuu) then ! addition in the u-direction l4 = l+4 fpint(l) = zero ! Clear location fac1 = tu(l4)-arg fac2 = arg-tu(l4-1) else choose_direction ! addition in the v-direction l4 = l+4-nuu fpint(l) = zero fac1 = tv(l4)-arg fac2 = arg-tv(l4-1) endif choose_direction ! Suitable location if (fac1<=(ten*fac2) .and. fac2<=(ten*fac1)) then ! Place knot place_knot: if (l<=nuu) then j = nu do i=l4,nu tu(j+1) = tu(j) j = j-1 end do tu(l4) = arg nu = nu+1 else place_knot ll = nrr+4 j = ll do i=l4,ll tv(j+1) = tv(j) j = j-1 end do tv(l4) = arg nv = nv+2 nrr = nrr+1 do i=5,ll j = i+nrr tv(j) = tv(i)+pi end do endif place_knot exit add_knot endif end do add_knot ! restart the computations with the new set of knots. end do compute_knots ! ************************************************************************************************************ ! part 2: determination of the smoothing bicubic spline. ! ************************************************************************************************************ ! we have determined the number of knots and their position. we now compute the coefficients of the ! smoothing spline sp(u,v). the observation matrix a is extended by the rows of a matrix, expressing ! that sp(u,v) must be a constant function in the variable v and a cubic polynomial in the variable u. ! the corresponding weights of these additional rows are set to 1/(p). iteratively we than have to ! determine the value of p such that f(p) = sum((w(i)*(z(i)-sp(u(i),v(i))))**2) be = s. ! we already know that the least-squares polynomial corresponds to p=0, and that the least-squares ! bicubic spline corresponds to p=infinity. the iteration process makes use of rational interpolation. ! since f(p) is a convex and strictly decreasing function of p, it can be approximated by a rational ! function of the form r(p) = (u*p+v)/(p+w). three values of p (p1,p2,p3) with corresponding values of ! f(p) (f1=f(p1)-s,f2=f(p2)-s,f3=f(p3)-s) are used to calculate the new value of p such that r(p)=s. ! convergence is guaranteed by taking f1>0,f3<zero ! ************************************************************************************************************ ! evaluate the discontinuity jumps of the 3-th order derivative of the b-splines at the knots: call fpdisc(tu,nu,5,bu,nuest) ! tu(l),l=5,...,nu-4. call fpdisc(tv,nv,5,bv,nvest) ! tv(l),l=5,...,nv-4. ! initial value for p. p1 = zero f1 = sup-s p3 = -one f3 = fpms p = sum(a(:ncof,1))/real(ncof,RKIND) ! find the bandwidth of the extended observation matrix. iband4 = min(ncof,iband+ipar1) iband3 = iband4 -1 check1 = .false. check3 = .false. nuu = nu4-iopt3-1 ! iteration process to find the root of f(p)=s. iterations: do iter=1,maxit pinv = one/p ! store the triangularized observation matrix into q. ff(1:ncof) = f(1:ncof) q(1:ncof,1:iband4) = zero q(1:ncof,1:iband) = a(1:ncof,1:iband) ! extend the observation matrix with the rows of a matrix, expressing ! that for u=constant sp(u,v) must be a constant function. u_constant: do i=5,nv4 ii = i-4 row(1:nvv) = zero ll = ii do l=1,5 if (ll>nvv) ll=1 row(ll) = row(ll)+bv(ii,l) ll = ll+1 end do do j=1,nuu ! initialize the new row. h(1:iband) = zero ! fill in the non-zero elements of the row. jrot records the column ! number of the first non-zero element in the row. if (j>iopt2) then h(1:nvv) = row(1:nvv) jrot = ipar1+1+(j-iopt2-1)*nvv elseif (j==2) then cs(3:5) = matmul(cosi(3:5,1:nvv),row(1:nvv)) h(1:2) = cs(1:2)*ratio h(3:5) = cs(3:5) jrot = 2 else cs(1:2) = matmul(cosi(1:2,1:nvv),row(1:nvv)) h(1:2) = cs(1:2) jrot = 2 end if h(1:iband) = h(1:iband)*pinv zi = zero ! rotate the new row into triangle by givens transformations. rot_new_row: do irot=jrot,ncof piv = h(1) i2 = min(iband1,ncof-irot) if (equal(piv,zero)) then if (i2<=0) exit rot_new_row else ! calculate the parameters of the givens transformation. call fpgivs(piv,q(irot,1),co,si) ! apply that givens transformation to the right hand side. call fprota(co,si,zi,ff(irot)) if (i2==0) exit rot_new_row ! apply that givens transformation to the left hand side. call fprota(co,si,h(2:i2+1),q(irot,2:i2+1)) endif h(1:i2+1) = [h(2:i2+1),zero] end do rot_new_row end do end do u_constant ! extend the observation matrix with the rows of a matrix expressing ! that for v=constant. sp(u,v) must be a cubic polynomial. v_constant: do i=5,nu4 ii = i-4 do j=1,nvv ! initialize the new row h(1:iband4) = zero ! fill in the non-zero elements of the row. jrot records the column ! number of the first non-zero element in the row. j1 = 1 fill_in: do l=1,5 il = ii+l-1 if (il==nu4 .and. iopt3/=0) then cycle fill_in elseif (il>iopt2+1) then h(j1) = bu(ii,l) j1 = j1+nvv elseif (il<0) then h(1) = bu(ii,l) j1 = j+1 elseif (il==0) then h(1) = h(1)+bu(ii,l) h(2:3) = bu(ii,l)*cosi(1:2,j) j1 = j+3 elseif (il>0) then h(1) = h(1)+bu(ii,l) h(2:3) = bu(ii,l)*cosi(1:2,j)*ratio h(4:6) = bu(ii,l)*cosi(3:5,j) j1 = j+6 endif end do fill_in h(:iband4) = h(:iband4)*pinv zi = zero jrot = merge(ipar1+(ii-iopt2-2)*nvv+j,1,ii>iopt2+1) ! rotate the new row into triangle by givens transformations. do irot=jrot,ncof piv = h(1) i2 = min(iband3,ncof-irot) if (equal(piv,zero)) then if (i2<=0) exit h(1:i2+1) = [h(2:i2+1),zero] else ! calculate the parameters of the givens transformation. call fpgivs(piv,q(irot,1),co,si) ! apply that givens transformation to the right hand side. call fprota(co,si,zi,ff(irot)) if (i2==0) exit ! apply that givens transformation to the left hand side. call fprota(co,si,h(2:i2+1),q(irot,2:i2+1)) h(1:i2+1) = [h(2:i2+1),zero] endif end do end do end do v_constant ! find dmax, the maximum value for the diagonal elements in the reduced triangle. dmax = max(zero,maxval(q(1:ncof,1))) ! check whether the matrix is rank deficient. sigma = eps*dmax if (all(q(1:ncof,1)>sigma)) then ! backward substitution in case of full rank. c(:ncof) = fpback(q,ff,ncof,iband4,ncc) rank = ncof else ! in case of rank deficiency, find the minimum norm solution. lwest = ncof*iband4+ncof+iband4 if (lwest>lwrk) then ier = lwest return end if lf = 1 lh = lf+ncof la = lh+iband4 call fprank(q,ff,ncof,iband4,ncc,sigma,c,sq,rank,wrk(la),wrk(lf),wrk(lh)) endif q(:ncof,1) = q(:ncof,1)/dmax ! find the coefficients in the standard b-spline representation of the polar spline. call fprppo(nu,nv,iopt2,iopt3,cosi,ratio,c,ff,ncoff) ! compute f(p). fp = zero get_fp: do num = 1,nreg num1 = num-1 lu = num1/nvv lv = num1-lu*nvv jrot = lu*nv4+lv in = index(num) do while (in/=0) store = zero i1 = jrot do i=1,4 store = store + spu(in,i)*dot_product(spv(in,1:4),c(i1+1:i1+4)) i1 = i1+nv4 end do fp = fp+(w(in)*(z(in)-store))**2 in = nummer(in) end do end do get_fp ! test whether the approximation sp(u,v) is an acceptable solution fpms = fp-s if (abs(fpms)<=acc) then if (ncof/=rank) ier = -rank return end if ! find the new value of p and carry out one more step. call root_finding_iterate(p1,f1,p2,f2,p3,f3,p,fpms,acc,check1,check3,success) if (.not.success) then ier = FITPACK_S_TOO_SMALL return end if end do iterations ! the maximum allowable number of iterations has been reached. ier = FITPACK_MAXIT return end subroutine fppola ! three values of p (p1,p2,p3) with corresponding values of ! f(p) (f1=f(p1)-s,f2=f(p2)-s,f3=f(p3)-s) are used to calculate the new value of p ! such that r(p)=s. convergence is guaranteed by taking f1>0,f3<zero elemental subroutine root_finding_iterate(p1,f1,p2,f2,p3,f3,p,fpms,acc,check1,check3,success) real(RKIND), intent(inout) :: p1,f1,p2,f2,p3,f3,p real(RKIND), intent(in) :: fpms,acc logical, intent(inout) :: check1,check3 logical, intent(out) :: success ! set constants real(RKIND), parameter :: con1 = 0.1e0_RKIND real(RKIND), parameter :: con9 = 0.9e0_RKIND real(RKIND), parameter :: con4 = 0.4e-01_RKIND success = .true. p2 = p f2 = fpms if (.not.check3) then if ((f2-f3)>acc) then check3=f2<zero else ! our initial choice of p is too large. p3 = p2 f3 = f2 p = p*con4 if (p<=p1) p=p1*con9 + p2*con1 return endif endif if (.not.check1) then if ((f1-f2)>acc) then check1 = f2>zero else ! our initial choice of p is too small p1 = p2 f1 = f2 p = p/con4 if (p3>=zero .and. p>=p3) p = p2*con1 + p3*con9 return endif endif ! test whether the iteration process proceeds as theoretically expected. if (f2>=f1 .or. f2<=f3) then success = .false. return else ! find the new value of p. call fprati(p1,f1,p2,f2,p3,f3,p) endif end subroutine root_finding_iterate ! subroutine fprank finds the minimum norm solution of a leastsquares problem in case of ! rank deficiency. pure subroutine fprank(a,f,n,m,na,tol,c,sq,rank,aa,ff,h) ! ..scalar arguments.. integer, intent(in) :: n ! the dimension of a. integer, intent(in) :: m ! the bandwidth of a. integer, intent(in) :: na integer, intent(out) :: rank ! the rank of matrix a. real(RKIND), intent(in) :: tol ! a threshold to determine the rank of a. real(RKIND), intent(out) :: sq ! the contribution of reducing the rank to the sum of squared residuals. ! ..array arguments.. real(RKIND), intent(out) :: c(n) ! the minimum norm solution. real(RKIND), intent(out) :: a(na,m) ! the non-zero elements of the observation matrix after ! triangularization by givens transformations. real(RKIND), intent(out) :: f(n) ! the transformed right hand side. real(RKIND), intent(inout) :: aa(n,m),ff(n),h(m) ! working arrays ! ..local scalars.. integer :: i,ii,ij,i1,i2,j,jj,j1,j2,j3,k,kk,m1,nl real(RKIND) :: cos,fac,piv,sin,yi real(RKIND) :: store,stor1,stor2,stor3 ! .. m1 = m-1 ! the rank deficiency nl is considered to be the number of sufficient small diagonal elements of a. nl = 0 sq = zero rows: do i=1,n if (a(i,1)>tol) cycle rows ! if a sufficient small diagonal element is found, we put it to zero. the remainder of the row ! corresponding to that zero diagonal element is then rotated into triangle by givens rotations . ! the rank deficiency is increased by one. nl = nl+1 if (i==n) cycle rows yi = f(i) h(1:m) = [a(i,2:m),zero] i1 = i+1 in_row: do ii=i1,n i2 = min(n-ii,m1) piv = h(1) if (equal(piv,zero)) then if (i2==0) exit in_row h(1:i2) = h(2:i2+1) else call fpgivs(piv,a(ii,1),cos,sin) call fprota(cos,sin,yi,f(ii)) if (i2==0) exit in_row do j=1,i2 j1 = j+1 call fprota(cos,sin,h(j1),a(ii,j1)) h(j) = h(j1) end do endif h(i2+1) = zero end do in_row ! add to the sum of squared residuals the contribution of deleting ! the row with small diagonal element. sq = sq+yi**2 end do rows ! rank denotes the rank of a. rank = n-nl ! let b denote the (rank*n) upper trapezoidal matrix which can be obtained from the (n*n) upper ! triangular matrix a by deleting the rows and interchanging the columns corresponding to a zero ! diagonal element. if this matrix is factorized using givens transformations as b = (r) (u) where ! r is a (rank*rank) upper triangular matrix, ! u is a (rank*n) orthonormal matrix ! then the minimal least-squares solution c is given by c = b' v, where v is the solution of the ! system (r) (r)' v = g and g denotes the vector obtained from the old right hand side f, by ! removing the elements corresponding to a zero diagonal element of a. ! initialization. aa(1:rank,1:m) = zero ! form in aa the upper triangular matrix obtained from a by removing rows and columns with zero ! diagonal elements. form in ff the new right hand side by removing the elements of the old right ! hand side corresponding to a deleted row. ii = 0 make_U: do i=1,n if (a(i,1)<=tol) cycle make_U ii = ii+1 ff(ii) = f(i) aa(ii,1) = a(i,1) jj = ii kk = 1 j = i j1 = min(j-1,m1) if (j1/=0) then ! probably unnecessary do k=1,j1 j = j-1 if (a(j,1)>tol) then kk = kk+1 jj = jj-1 aa(jj,kk) = a(j,k+1) endif end do endif end do make_U ! form successively in h the columns of a with a zero diagonal element. ii = 0 make_h: do i=1,n ii = ii+1 if (a(i,1)>tol) cycle make_h ii = ii-1 if (ii==0) cycle make_h jj = 1 j = i j1 = min(j-1,m1) do k=1,j1 j = j-1 if (a(j,1)>tol) then h(jj) = a(j,k+1) jj = jj+1 endif end do h(jj:m) = zero ! rotate this column into aa by givens transformations. jj = ii rotate_col: do i1=1,ii j1 = min(jj-1,m1) piv = h(1) if (not_equal(piv,zero)) then call fpgivs(piv,aa(jj,1),cos,sin) if (j1==0) cycle make_h kk = jj do j2=1,j1 j3 = j2+1 kk = kk-1 call fprota(cos,sin,h(j3),aa(kk,j3)) h(j2) = h(j3) end do else if (j1==0) cycle make_h do j2=1,j1 j3 = j2+1 h(j2) = h(j3) end do endif jj = jj-1 h(j3) = zero end do rotate_col end do make_h ! solve the system (aa) (f1) = ff ff(rank) = ff(rank)/aa(rank,1) i = rank-1 if (i/=0) then do j=2,rank store = ff(i) i1 = min0(j-1,m1) k = i do ii=1,i1 k = k+1 stor1 = ff(k) stor2 = aa(i,ii+1) store = store-stor1*stor2 end do stor1 = aa(i,1) ff(i) = store/stor1 i = i-1 end do endif ! solve the system (aa)' (f2) = f1 ff(1) = ff(1)/aa(1,1) if (rank>1) then do j=2,rank store = ff(j) i1 = min0(j-1,m1) k = j do ii=1,i1 k = k-1 stor1 = ff(k) stor2 = aa(k,ii+1) store = store-stor1*stor2 end do stor1 = aa(j,1) ff(j) = store/stor1 end do endif ! premultiply f2 by the transpoze of a. k = 0 do i=1,n store = zero if (a(i,1)>tol) k = k+1 j1 = min(i,m) kk = k ij = i+1 do j=1,j1 ij = ij-1 if (a(ij,1)>tol) then stor1 = a(ij,j) stor2 = ff(kk) store = store+stor1*stor2 kk = kk-1 endif end do c(i) = store end do ! add to the sum of squared residuals the contribution of putting ! to zero the small diagonal elements of matrix (a). stor3 = zero add_to_res: do i=1,n if (a(i,1)<=tol) then store = f(i) i1 = min(n-i,m1) if (i1>0) then do j=1,i1 ij = i+j stor1 = c(ij) stor2 = a(i,j+1) store = store-stor1*stor2 end do endif fac = a(i,1)*c(i) stor1 = a(i,1) stor2 = c(i) stor1 = stor1*stor2 stor3 = stor3+stor1*(stor1-store-store) endif end do add_to_res fac = stor3 sq = sq+fac return end subroutine fprank ! given three points (p1,f1),(p2,f2) and (p3,f3), function fprati gives the value of p such ! that the rational interpolating function of the form r(p) = (u*p+v)/(p+w) equals zero at p. elemental subroutine fprati(p1,f1,p2,f2,p3,f3,p) ! ..scalar arguments.. real(RKIND), intent(inout) :: p1,f1,p3,f3 real(RKIND), intent(in) :: p2,f2 real(RKIND), intent(out) :: p ! ..local scalars.. real(RKIND) :: h1,h2,h3 ! .. if (p3>zero) then h1 = f1*(f2-f3) h2 = f2*(f3-f1) h3 = f3*(f1-f2) p = -(p1*p2*h3+p2*p3*h1+p3*p1*h2)/(p1*h1+p2*h2+p3*h3) else ! value of p in case p3 = infinity. p = (p1*(f1-f3)*f2-p2*(f2-f3)*f1)/((f1-f2)*f3) end if ! adjust the value of p1,f1,p3 and f3 such that f1 > 0 and f3 < 0. if (f2>=zero) then p1 = p2 f1 = f2 else p3 = p2 f3 = f2 endif return end subroutine fprati pure subroutine fpregr(iopt,x,mx,y,my,z,mz,xb,xe,yb,ye, & kx,ky,s,nxest,nyest,tol,maxit,nc,nx,tx,ny,ty,c,fp,fp0,fpold, & reducx,reducy,fpintx,fpinty,lastdi,nplusx,nplusy,nrx,nry, & nrdatx,nrdaty,wrk,lwrk,ier) ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: xb,xe,yb,ye,s,tol real(RKIND), intent(inout) :: fp,fp0,fpold,reducx,reducy integer , intent(in) :: iopt,mx,my,mz,kx,ky,nxest,nyest,maxit,nc,lwrk integer , intent(inout) :: nx,ny,lastdi,nplusx,nplusy,ier ! ..array arguments.. real(RKIND), intent(in) :: x(mx),y(my),z(mz) real(RKIND), intent(inout) :: c(nc),tx(nxest),ty(nyest),fpintx(nxest),fpinty(nyest),wrk(lwrk) integer , intent(inout) :: nrdatx(nxest),nrdaty(nyest),nrx(mx),nry(my) ! ..local scalars real(RKIND) :: acc,fpms,f1,f2,f3,p,p1,p2,p3,rn integer :: i,ifbx,ifby,ifsx,ifsy,iter,j,kx1,kx2,ky1,ky2,k3,l,lax,lay,lbx,lby,lq,lri,lsx,& lsy,mk1,mm,mpm,mynx,ncof,nk1x,nk1y,nmaxx,nmaxy,nminx,nminy,nplx,nply,npl1,nrintx, & nrinty,nxe,nxk,nye logical :: check1,check3,success ! we partition the working space. kx1 = kx+1 ky1 = ky+1 kx2 = kx1+1 ky2 = ky1+1 lsx = 1 lsy = lsx+mx*kx1 lri = lsy+my*ky1 mm = max(nxest,my) lq = lri+mm mynx = nxest*my lax = lq+mynx nxk = nxest*kx2 lbx = lax+nxk lay = lbx+nxk lby = lay+nyest*ky2 ! ***** ! part 1: determination of the number of knots and their position. ! ***** ! given a set of knots we compute the least-squares spline sinf(x,y) and the corresponding sum of ! squared residuals fp = f(p=inf). ! if iopt(1)=-1 sinf(x,y) is the requested approximation. ! if iopt(1)>=0 we check whether we can accept the knots: ! if fp <= s we will continue with the current set of knots. ! if fp > s we will increase the number of knots and compute the corresponding least-squares ! spline until finally fp <= s. ! the initial choice of knots depends on the value of s and iopt. ! if s=0 we have spline interpolation; in that case the number of knots equals ! nmaxx = mx+kx+1 and nmaxy = my+ky+1. ! if s>0 and ! iopt(1)=0 we first compute the least-squares polynomial of degree kx in x and ky in y; ! nx=nminx=2*kx+2 and ny=nymin=2*ky+2. ! iopt(1)=1 we start with the set of knots found at the last call of the routine, except for the ! case that s > fp0; then we compute the least-squares polynomial directly. ! ***** ! determine the number of knots for polynomial approximation. nminx = 2*kx1 nminy = 2*ky1 bootstrap: if (iopt>=0) then ! acc denotes the absolute tolerance for the root of f(p)=s. acc = tol*s ! find nmaxx and nmaxy which denote the number of knots in x- and y- ! direction in case of spline interpolation. nmaxx = mx+kx1 nmaxy = my+ky1 ! find nxe and nye which denote the maximum number of knots allowed in each direction nxe = min(nmaxx,nxest) nye = min(nmaxy,nyest) interpolating: if (s<=zero) then ! if s = 0, s(x,y) is an interpolating spline. nx = nmaxx ny = nmaxy ! test whether the required storage space exceeds the available one. if (ny>nyest .or. nx>nxest) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! find the position of the interior knots in case of interpolation. ! the knots in the x-direction. mk1 = mx-kx1 if (mk1/=0) then k3 = kx/2 i = kx1+1 j = k3+2 do l=1,mk1 tx(i) = merge( x(j) , (x(j)+x(j-1))*half , k3*2/=kx) i = i+1 j = j+1 end do endif ! the knots in the y-direction. mk1 = my-ky1 if (mk1/=0) then k3 = ky/2 i = ky1+1 j = k3+2 do l=1,mk1 ty(i) = merge( y(j) , (y(j)+y(j-1))*half , k3*2/=ky) i = i+1 j = j+1 end do endif else interpolating ! if s > 0 our initial choice of knots depends on the value of iopt. use_last_call: if (iopt/=0 .and. fp0>s) then ! if iopt=1 and fp0 > s we start computing the least- squares spline ! according to the set of knots found at the last call of the routine. ! we determine the number of grid coordinates x(i) inside each knot ! interval (tx(l),tx(l+1)). l = kx2 j = 1 nrdatx(1) = 0 mpm = mx-1 do i=2,mpm nrdatx(j) = nrdatx(j)+1 if (x(i)>=tx(l)) then nrdatx(j) = nrdatx(j)-1 l = l+1 j = j+1 nrdatx(j) = 0 endif end do ! we determine the number of grid coordinates y(i) inside each knot ! interval (ty(l),ty(l+1)). l = ky2 j = 1 nrdaty(1) = 0 mpm = my-1 do i=2,mpm nrdaty(j) = nrdaty(j)+1 if (y(i)>=ty(l)) then nrdaty(j) = nrdaty(j)-1 l = l+1 j = j+1 nrdaty(j) = 0 endif end do else use_last_call ! if iopt=0 or iopt=1 and s>=fp0, we start computing the least-squares ! polynomial of degree kx in x and ky in y (which is a spline without ! interior knots). nx = nminx ny = nminy nrdatx(1) = mx-2 nrdaty(1) = my-2 lastdi = KNOT_DIM_NONE nplusx = 0 nplusy = 0 fp0 = zero fpold = zero reducx = zero reducy = zero endif use_last_call endif interpolating endif bootstrap mpm = mx+my ifsx = 0 ifsy = 0 ifbx = 0 ifby = 0 p = -one ! main loop for the different sets of knots.mpm=mx+my is a save upper ! bound for the number of trials. iter = 0 main_loop: do while (iter<=mpm) iter = iter+1 if (nx==nminx .and. ny==nminy) ier = FITPACK_LEASTSQUARES_OK ! find nrintx (nrinty) which is the number of knot intervals in the ! x-direction (y-direction). nrintx = nx-nminx+1 nrinty = ny-nminy+1 ! find ncof, the number of b-spline coefficients for the current set of knots. nk1x = nx-kx1 nk1y = ny-ky1 ncof = nk1x*nk1y ! find the position of the additional knots which are needed for the ! b-spline representation of s(x,y). tx(1:kx1) = xb tx(nx-kx:nx) = xe ty(1:ky1) = yb ty(ny-ky:ny) = ye ! find the least-squares spline sinf(x,y) and calculate for each knot ! interval tx(j+kx)<=x<=tx(j+kx+1) (ty(j+ky)<=y<=ty(j+ky+1)) the sum ! of squared residuals fpintx(j),j=1,2,...,nx-2*kx-1 (fpinty(j),j=1,2, ! ...,ny-2*ky-1) for the data points having their absciss (ordinate)- ! value belonging to that interval. ! fp gives the total sum of squared residuals. call fpgrre(ifsx,ifsy,ifbx,ifby,x,mx,y,my,z,mz,kx,ky,tx,nx,ty, & ny,p,c,nc,fp,fpintx,fpinty,mm,mynx,kx1,kx2,ky1,ky2,wrk(lsx), & wrk(lsy),wrk(lri),wrk(lq),wrk(lax),wrk(lay),wrk(lbx),wrk(lby), & nrx,nry) if (ier==FITPACK_LEASTSQUARES_OK) fp0 = fp ! SUCCESS! the least-squares spline is an acceptable solution. fpms = fp-s if (iopt<0 .or. abs(fpms)<acc) return ! if f(p=inf) < s, we accept the choice of knots. if (fpms<zero) exit main_loop ! if nx=nmaxx and ny=nmaxy, sinf(x,y) is an interpolating spline. if (nx==nmaxx .and. ny==nmaxy) then ier = FITPACK_INTERPOLATING_OK fp = zero return end if ! increase the number of knots. ! if nx=nxe and ny=nye we cannot further increase the number of knots ! because of the storage capacity limitation. if (nx==nxe .and. ny==nye) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ier = FITPACK_OK ! adjust the parameter reducx or reducy according to the direction ! in which the last added knots were located. select case (lastdi) case (KNOT_DIM_1); reducx = fpold-fp case (KNOT_DIM_2); reducy = fpold-fp end select ! store the sum of squared residuals for the current set of knots. fpold = fp ! find nplx, the number of knots we should add in the x-direction. nplx = 1 if (nx/=nminx) then npl1 = nplusx*2 rn = nplusx if (reducx>acc) npl1 = int(rn*fpms/reducx) nplx = min(nplusx*2,max(npl1,nplusx/2,1)) endif ! find nply, the number of knots we should add in the y-direction. nply = 1 if (ny/=nminy) then npl1 = nplusy*2 rn = nplusy if (reducy>acc) npl1 = int(rn*fpms/reducy) nply = min(nplusy*2,max(npl1,nplusy/2,1)) endif ! test whether we are going to add knots in the x- or x-direction. lastdi = new_knot_dimension(nx,nplx,nxe,ny,nply,nye,lastdi) choose_dim: if (lastdi==KNOT_DIM_2) then ! addition in the y-direction. nplusy = nply ifsy = 0 add_y_knots: do l=1,nplusy ! add a new knot in the v-direction. call fpknot(y,my,ty,ny,fpinty,nrdaty,nrinty,nyest,1) ! test whether we cannot further increase the number of knots in the y-direction. if (ny==nye) exit add_y_knots end do add_y_knots else choose_dim ! addition in the x-direction. nplusx = nplx ifsx = 0 add_x_knots: do l=1,nplusx ! add a new knot in the u-direction call fpknot(x,mx,tx,nx,fpintx,nrdatx,nrintx,nxest,1) ! test whether we cannot further increase the number of knots in the x-direction. if (nx==nxe) exit add_x_knots end do add_x_knots endif choose_dim ! restart the computations with the new set of knots. end do main_loop ! test whether the least-squares polynomial is a solution of our ! approximation problem. if (ier==FITPACK_LEASTSQUARES_OK) return ! ********************************************************************************************** ! part 2: determination of the smoothing spline sp(x,y) ! ********************************************************************************************** ! we have determined the number of knots and their position. we now compute the b-spline ! coefficients of the smoothing spline sp(x,y). this smoothing spline varies with the parameter ! p in such a way that ! f(p) = sumi=1,mx(sumj=1,my((z(i,j)-sp(x(i),y(j)))**2) ! is a continuous, strictly decreasing function of p. moreover the least-squares polynomial ! corresponds to p=0 and the least-squares spline to p=infinity. iteratively we then have to ! determine the positive value of p such that f(p)=s. the process which is proposed here makes ! use of rational interpolation. f(p) is approximated by a rational function r(p)=(u*p+v)/(p+w); ! three values of p (p1,p2,p3) with corresponding values of f(p) (f1=f(p1)-s,f2=f(p2)-s, ! f3=f(p3)-s) are used to calculate the new value of p such that r(p)=s. ! convergence is guaranteed by taking f1 > 0 and f3 < 0. ! ********************************************************************************************** ! initial value for p. p1 = zero f1 = fp0-s p3 = -one f3 = fpms p = one check1 = .false. check3 = .false. ! iteration process to find the root of f(p)=s. root_iterations: do iter = 1,maxit ! find the smoothing spline sp(x,y) and the corresponding sum of ! squared residuals fp. call fpgrre(ifsx,ifsy,ifbx,ifby,x,mx,y,my,z,mz,kx,ky,tx,nx,ty, & ny,p,c,nc,fp,fpintx,fpinty,mm,mynx,kx1,kx2,ky1,ky2,wrk(lsx), & wrk(lsy),wrk(lri),wrk(lq),wrk(lax),wrk(lay),wrk(lbx),wrk(lby), & nrx,nry) ! test whether the approximation sp(x,y) is an acceptable solution. fpms = fp-s; if (abs(fpms)<acc) return ! find the new value of p and carry out one more step. call root_finding_iterate(p1,f1,p2,f2,p3,f3,p,fpms,acc,check1,check3,success) if (.not.success) then ier = FITPACK_S_TOO_SMALL return end if end do root_iterations ! Maximum number of iterations reached. ier = FITPACK_MAXIT return end subroutine fpregr ! Choose the dimension the next knot should be added on elemental integer function new_knot_dimension(n1,n1add,n1max,n2,n2add,n2max,last) result(dir) integer, intent(in) :: n1,n2 ! Number of knots in both dimensions integer, intent(in) :: n1add,n2add ! Number of knots ADDED in either dimension integer, intent(in) :: n1max,n2max ! Max number of knots in either dimension integer, intent(in) :: last ! Last dimension used if ( n2<n2max .and. ( n2add<=n1add .or. n1>=n1max .or. last==KNOT_DIM_NONE ) )then dir = KNOT_DIM_2 else dir = KNOT_DIM_1 end if end function new_knot_dimension ! subroutine fprota applies a givens rotation to a and b. elemental subroutine fprota(cos,sin,a,b) ! ..scalar arguments.. real(RKIND), intent(in) :: cos,sin real(RKIND), intent(inout) :: a,b ! ..local scalars.. real(RKIND) :: stor1,stor2 ! .. stor1 = a stor2 = b b = cos*stor2+sin*stor1 a = cos*stor1-sin*stor2 return end subroutine fprota ! given the coefficients of a constrained bicubic spline, as determined in subroutine fppola, ! fprppo calculates the coefficients in the standard b-spline representation of bicubic splines. pure subroutine fprppo(nu,nv,if1,if2,cosi,ratio,c,f,ncoff) ! ..scalar arguments.. real(RKIND), intent(in) :: ratio integer, intent(in) :: if1,if2,nu,nv,ncoff ! ..array arguments real(RKIND), intent(inout) :: c(ncoff),f(ncoff) real(RKIND), intent(in) :: cosi(5,nv) ! ..local scalars.. integer :: i,iopt,ii,j,k,l,nu4,nvv ! .. nu4 = nu-4 nvv = nv-7 iopt = if1+1 f = zero i = 0 j = 0 main_loop: do l=1,nu4 ii = i if (l==nu4 .and. if2/=0) then exit main_loop elseif (l>iopt) then do k=1,nvv i = i+1 j = j+1 f(i) = c(j) end do elseif (l==1) then do k=1,nvv i = i+1 f(i) = c(1) end do j = 1 elseif (l==2) then do k=1,nvv i = i+1 f(i) = c(1)+c(2)*cosi(1,k)+c(3)*cosi(2,k) end do j = 3 elseif (l==3) then do k=1,nvv i = i+1 f(i) = c(1)+ratio*(c(2)*cosi(1,k)+c(3)*cosi(2,k))+ & c(4)*cosi(3,k)+c(5)*cosi(4,k)+c(6)*cosi(5,k) end do j = 6 end if do k=1,3 ii = ii+1 i = i+1 f(i) = f(ii) end do end do main_loop c = f return end subroutine fprppo ! given the coefficients of a spherical spline function, subroutine fprpsp calculates the ! coefficients in the standard b-spline representation of this bicubic spline. pure subroutine fprpsp(nt,np,co,si,c,f,ncoff) ! .. ! ..scalar arguments integer, intent(in) :: nt,np,ncoff ! ..array arguments real(RKIND), intent(in) :: co(np),si(np) real(RKIND), intent(inout) :: c(ncoff),f(ncoff) ! f is a working array ! ..local scalars real(RKIND) :: cn,c1,c2,c3 integer :: i,ii,j,k,l,ncof,npp,np4,nt4 ! .. nt4 = nt-4 np4 = np-4 npp = np4-3 ncof = 6+npp*(nt4-4) c1 = c(1) cn = c(ncof) f(1:np4) = c1 f(ncoff-np4+1:ncoff) = cn i = np4 j = 1 do l=3,nt4 ii = i if (l==3 .or. l==nt4) then if (l==nt4) c1 = cn c2 = c(j+1) c3 = c(j+2) j = j+2 do k=1,npp i = i+1 f(i) = c1+c2*co(k)+c3*si(k) end do else do k=1,npp i = i+1 j = j+1 f(i) = c(j) end do endif do k=1,3 ii = ii+1 i = i+1 f(i) = f(ii) end do end do c(1:ncoff) = f(1:ncoff) return end subroutine fprpsp ! subroutine fpseno fetches a branch of a triply linked tree the information of which is kept ! in the arrays up,left,right and info. ! the branch has a specified length nbind and is determined by the parameter merk which points to ! its terminal node. the information field of the nodes of this branch is stored in the array ! ibind. on exit merk points to a new branch of length nbind or takes the value 1 if no such ! branch was found. pure subroutine fpseno(maxtr,up,left,right,info,merk,ibind,nbind) ! ..scalar arguments.. integer, intent(in) :: maxtr ! Tree array sizes integer, intent(inout) :: merk ! (in) terminal node of the branch integer, intent(in) :: nbind ! ..array arguments.. integer, intent(in) :: up(maxtr),left(maxtr),right(maxtr),info(maxtr) integer, intent(out) :: ibind(nbind) ! ..scalar arguments.. integer :: i,j,k ! .. k = merk j = nbind do i=1,nbind ibind(j) = info(k) k = up(k) j = j-1 end do do k = right(merk) if (k/=0) exit merk = up(merk) if (merk<=1) return end do do merk = k k = left(merk) if (k==0) exit end do end subroutine fpseno pure subroutine fpspgr(iopt,ider,u,mu,v,mv,r,mr,r0,r1,s, & nuest,nvest,tol,maxit,nc,nu,tu,nv,tv,c,fp,fp0,fpold,reducu, & reducv,fpintu,fpintv,dr,step,lastdi,nplusu,nplusv,lastu0, & lastu1,nru,nrv,nrdatu,nrdatv,wrk,lwrk,ier) ! .. ! ..scalar arguments.. integer, intent(in) :: mu,mv,mr,nuest,nvest,maxit,nc,lwrk integer, intent(inout) :: nu,nv,lastdi,nplusu,nplusv,lastu0,lastu1 integer, intent(out) :: ier real(RKIND), intent(in) :: r0,r1,s,tol real(RKIND), intent(inout) :: fp,fp0,fpold,reducu,reducv ! ..array arguments.. integer, intent(in) :: iopt(3),ider(4) integer, intent(inout) :: nru(mu),nrv(mv),nrdatu(nuest),nrdatv(nvest) real(RKIND), intent(in) :: u(mu),v(mv),r(mr) real(RKIND), intent(inout) :: fpintu(nuest),fpintv(nvest),dr(6),tu(nuest),tv(nvest),c(nc),& wrk(lwrk),step(2) ! ..local scalars.. real(RKIND) :: acc,fpms,f1,f2,f3,p,p1,p2,p3,vb,ve,rmax,rmin,rn integer :: i,ifbu,ifbv,ifsu,ifsv,istart,iter,i1,i2,j,ju,ktu,l,l1,l2,l3,l4,mpm,mumin, & mu0,mu1,nn,nplu,nplv,npl1,nrintu,nrintv,nue,numax,nve,nvmax ! ..local arrays.. logical :: check1,check3,success integer :: idd(4) real(RKIND) :: drr(6) ! set constants real(RKIND), parameter :: period = pi2 ! initialization ifsu = 0 ifsv = 0 ifbu = 0 ifbv = 0 p = -one mumin = 4 - merge(1,0,ider(1)>=0) - merge(1,0,iopt(2)==1 .and. ider(2)==1) & - merge(1,0,ider(3)>=0) - merge(1,0,iopt(3)==1 .and. ider(4)==1) if (mumin==0) mumin = 1 vb = v(1) ve = vb+period ! ***** ! part 1: determination of the number of knots and their position. ! ***** ! given a set of knots we compute the least-squares spline sinf(u,v) and the corresponding sum of ! squared residuals fp = f(p=inf). ! if iopt(1)=-1 sinf(u,v) is the requested approximation. ! if iopt(1)>=0 we check whether we can accept the knots: ! if fp <= s we will continue with the current set of knots. ! if fp > s we will increase the number of knots and compute the corresponding least-squares spline ! until finally fp <= s. ! the initial choice of knots depends on the value of s and iopt. ! if s=0 we have spline interpolation; in that case the number of knots in the u-direction equals ! nu=numax=mu+6+iopt(2)+iopt(3) and in the v-direction nv=nvmax=mv+7. ! if s>0 and ! iopt(1)=0 we first compute the least-squares polynomial,i.e. a spline without interior knots: ! nu=8 ; nv=8. ! iopt(1)=1 we start with the set of knots found at the last call of the routine, except for the ! case that s > fp0; then we compute the least-squares polynomial directly. ! ***** if (iopt(1)>=0) then ! acc denotes the absolute tolerance for the root of f(p)=s. acc = tol*s ! numax and nvmax denote the number of knots needed for interpolation. numax = mu+6+iopt(2)+iopt(3) nvmax = mv+7 nue = min(numax,nuest) nve = min(nvmax,nvest) if (s<=zero) then ! if s = 0, s(u,v) is an interpolating spline. nu = numax nv = nvmax ! test whether the required storage space exceeds the available one. if (nu>nuest .or. nv>nvest) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! find the position of the knots in the v-direction. tv(1:mv+7) = [v(mv-2:mv)-period,v(1:mv),ve,v(2:4)+period] ! if not all the derivative values g(i,j) are given, we will first ! estimate these values by computing a least-squares spline idd = ider if(idd(1)==0) idd(1) = 1 if(idd(1)>0) dr(1) = r0 if(idd(3)==0) idd(3) = 1 if(idd(3)>0) dr(4) = r1 if(ider(1)<0 .or. (iopt(2)/=0 .and. ider(2)==0) .or. & ider(3)<0 .or. (iopt(3)/=0 .and. ider(4)==0)) then ! we set up the knots in the u-direction for computing the least-squares spline. i1 = 3 i2 = mu-2 nu = 4 do i=1,mu if (i1>i2) exit nu = nu+1 tu(nu) = u(i1) i1 = i1+2 end do do i=1,4 tu(i) = zero nu = nu+1 tu(nu) = pi end do ! we compute the least-squares spline for estimating the derivatives. call fpopsp(ifsu,ifsv,ifbu,ifbv,u,mu,v,mv,r,mr,r0,r1,dr,iopt,idd, & tu,nu,tv,nv,nuest,nvest,p,step,c,nc,fp,fpintu,fpintv,nru,nrv, & wrk,lwrk) ifsu = 0 end if ! if all the derivatives at the origin are known, we compute the ! interpolating spline. ! we set up the knots in the u-direction, needed for interpolation. nn = numax-8 if (nn/=0) then ju = 2-iopt(2) do l=1,nn tu(l+4) = u(ju) ju = ju+1 end do nu = numax l = nu do i=1,4 tu(i) = zero tu(l) = pi l = l-1 end do endif ! we compute the interpolating spline. call fpopsp(ifsu,ifsv,ifbu,ifbv,u,mu,v,mv,r,mr,r0,r1,dr,iopt,idd, & tu,nu,tv,nv,nuest,nvest,p,step,c,nc,fp,fpintu,fpintv,nru,nrv, & wrk,lwrk) ier = FITPACK_INTERPOLATING_OK fp = zero return else ! if s>0 our initial choice of knots depends on the value of iopt(1). ier = FITPACK_OK if (iopt(1)/=0) step = -step if (fp0<=s .or. iopt(1)==0) then ! if iopt(1)=0 or iopt(1)=1 and fp0 <= s,we start computing the least- ! squares polynomial (which is a spline without interior knots). ier = FITPACK_LEASTSQUARES_OK idd = [ider(1),1,ider(3),1] nu = 8 nv = 8 nrdatu(1) = mu-2+iopt(2)+iopt(3) nrdatv(1) = mv-1 lastdi = KNOT_DIM_NONE nplusu = 0 nplusv = 0 fp0 = zero fpold = zero reducu = zero reducv = zero else ! if iopt(1)=1 and fp0 > s we start computing the least-squares spline ! according to the set of knots found at the last call of the routine. ! we determine the number of grid coordinates u(i) inside each knot ! interval (tu(l),tu(l+1)). l = 5 j = 1 nrdatu(1) = 0 mu0 = 2-iopt(2) mu1 = mu-1+iopt(3) do i=mu0,mu1 nrdatu(j) = nrdatu(j)+1 if (u(i)>=tu(l)) then nrdatu(j) = nrdatu(j)-1 l = l+1 j = j+1 nrdatu(j) = 0 endif end do ! we determine the number of grid coordinates v(i) inside each knot ! interval (tv(l),tv(l+1)). l = 5 j = 1 nrdatv(1) = 0 do i=2,mv nrdatv(j) = nrdatv(j)+1 if (v(i)>=tv(l)) then nrdatv(j) = nrdatv(j)-1 l = l+1 j = j+1 nrdatv(j) = 0 endif end do idd = ider endif endif endif ! main loop for the different sets of knots.mpm=mu+mv is a safe upper ! bound for the number of iterations mpm = mu+mv iterations: do iter=1,mpm ! find nrintu (nrintv) which is the number of knot intervals in the ! u-direction (v-direction). nrintu = nu-7 nrintv = nv-7 ! find the position of the additional knots which are needed for the ! b-spline representation of s(u,v). i = nu do j=1,4 tu(j) = zero tu(i) = pi i = i-1 end do l1 = 4 l2 = l1 l3 = nv-3 l4 = l3 tv(l2) = vb tv(l3) = ve do j=1,3 l1 = l1+1 l2 = l2-1 l3 = l3+1 l4 = l4-1 tv(l2) = tv(l4)-pi2 tv(l3) = tv(l1)+pi2 end do ! find an estimate of the range of possible values for the optimal derivatives at the origin. ktu = max(mumin,nrdatu(1)+2-iopt(2)) if (ktu/=lastu0) then l = min(mr,mv*ktu) rmin = min(r0,minval(r(1:l))) rmax = max(r0,maxval(r(1:l))) step(1) = rmax-rmin lastu0 = ktu endif ktu = max(mumin,nrdatu(nrintu)+2-iopt(3)) if (ktu/=lastu1) then l = min(mr,mv*ktu) rmin = min(r1,minval(r(mr-l+1:mr))) rmax = max(r1,maxval(r(mr-l+1:mr))) step(2) = rmax-rmin lastu1 = ktu endif ! find the least-squares spline sinf(u,v). call fpopsp(ifsu,ifsv,ifbu,ifbv,u,mu,v,mv,r,mr,r0,r1,dr,iopt, & idd,tu,nu,tv,nv,nuest,nvest,p,step,c,nc,fp,fpintu,fpintv,nru, & nrv,wrk,lwrk) step = abs(step) if (ier==FITPACK_LEASTSQUARES_OK) fp0 = fp ! test whether the least-squares spline is an acceptable solution. if (iopt(1)<0) return fpms = fp-s; if (abs(fpms) < acc) return ! success ! if f(p=inf) < s, we accept the choice of knots. if (fpms<zero) exit iterations ! if nu=numax and nv=nvmax, sinf(u,v) is an interpolating spline if(nu==numax .and. nv==nvmax) then ier = FITPACK_INTERPOLATING_OK fp = zero return endif ! increase the number of knots. ! if nu=nue and nv=nve we cannot further increase the number of knots ! because of the storage capacity limitation. if (nu==nue .and. nv==nve) then ier = FITPACK_INSUFFICIENT_STORAGE return endif if(ider(1)==0) fpintu(1) = fpintu(1) +(r0-dr(1))**2 if(ider(3)==0) fpintu(nrintu) = fpintu(nrintu)+(r1-dr(4))**2 ier = FITPACK_OK ! adjust the parameter reducu or reducv according to the direction ! in which the last added knots were located. first_knot: if (lastdi==KNOT_DIM_NONE) then nplv = 3 idd(2) = ider(2) idd(4) = ider(4) fpold = fp else first_knot select case (lastdi) case (KNOT_DIM_1); reducu = fpold-fp case (KNOT_DIM_2); reducv = fpold-fp end select ! store the sum of squared residuals for the current set of knots. fpold = fp ! find nplu, the number of knots we should add in the u-direction. nplu = 1 if (nu/=8) then npl1 = nplusu*2 rn = nplusu if (reducu>acc) npl1 = int(rn*fpms/reducu) nplu = min(nplusu*2,max(npl1,nplusu/2,1)) endif ! find nplv, the number of knots we should add in the v-direction. nplv = 3 if (nv/=8) then npl1 = nplusv*2 rn = nplusv if (reducv>acc) npl1 = int(rn*fpms/reducv) nplv = min(nplusv*2,max(npl1,nplusv/2,1)) endif endif first_knot ! test whether we are going to add knots in the u- or v-direction. lastdi = new_knot_dimension(nu,nplu,nue,nv,nplv,nve,lastdi) choose_dim: if (lastdi==KNOT_DIM_2) then ! addition in the v-direction. nplusv = nplv ifsv = 0 add_v_knots: do l=1,nplusv ! add a new knot in the v-direction. call fpknot(v,mv,tv,nv,fpintv,nrdatv,nrintv,nvest,1) ! test whether we cannot further increase the number of knots in the v-direction. if (nv==nve) exit add_v_knots end do add_v_knots else choose_dim ! addition in the u-direction. nplusu = nplu ifsu = 0 istart = merge(1,0,iopt(2)==0) add_u_knots: do l=1,nplusu ! add a new knot in the u-direction call fpknot(u,mu,tu,nu,fpintu,nrdatu,nrintu,nuest,istart) ! test whether we cannot further increase the number of knots in the u-direction. if (nu==nue) exit add_u_knots end do add_u_knots endif choose_dim ! restart the computations with the new set of knots. end do iterations ! test whether the least-squares polynomial is a solution of our approximation problem. if (ier==FITPACK_LEASTSQUARES_OK) return ! ********************************************************************************************** ! part 2: determination of the smoothing spline sp(u,v) ! ********************************************************************************************** ! we have determined the number of knots and their position. we now compute the b-spline ! coefficients of the smoothing spline sp(u,v). this smoothing spline depends on the parameter ! p in such a way that ! f(p) = sumi=1,mu(sumj=1,mv((z(i,j)-sp(u(i),v(j)))**2) ! is a continuous, strictly decreasing function of p. moreover the least-squares polynomial ! corresponds to p=0 and the least-squares spline to p=infinity. then iteratively we have to ! determine the positive value of p such that f(p)=s. the process which is proposed here makes ! use of rational interpolation. f(p) is approximated by a rational function r(p)=(u*p+v)/(p+w); ! three values of p (p1,p2,p3) with corresponding values of f(p) (f1=f(p1)-s,f2=f(p2)-s, ! f3=f(p3)-s) are used to calculate the new value of p such that r(p)=s. ! convergence is guaranteed by taking f1 > 0 and f3 < 0. ! ********************************************************************************************** ! initial value for p. p1 = zero f1 = fp0-s p3 = -one f3 = fpms p = one drr = dr check1 = .false. check3 = .false. ! iteration process to find the root of f(p)=s. find_root: do iter = 1,maxit ! find the smoothing spline sp(u,v) and the corresponding sum f(p). call fpopsp(ifsu,ifsv,ifbu,ifbv,u,mu,v,mv,r,mr,r0,r1,drr,iopt, & idd,tu,nu,tv,nv,nuest,nvest,p,step,c,nc,fp,fpintu, & fpintv,nru,nrv,wrk,lwrk) ! test whether the approximation sp(u,v) is an acceptable solution. fpms = fp-s; if (abs(fpms)<=acc) return ! success ! find the new value of p and carry out one more step. call root_finding_iterate(p1,f1,p2,f2,p3,f3,p,fpms,acc,check1,check3,success) if (.not.success) then ier = FITPACK_S_TOO_SMALL return end if end do find_root ! max number of iterations exceeded ier = FITPACK_MAXIT return end subroutine fpspgr pure subroutine fpsphe(iopt,m,teta,phi,r,w,s,ntest,npest,eta,tol,maxit, & ib1,ib3,nc,ncc,intest,nrest,nt,tt,np,tp,c,fp,sup,fpint,coord,f, & ff,row,coco,cosi,a,q,bt,bp,spt,spp,h,index,nummer,wrk,lwrk,ier) ! .. ! ..scalar arguments.. integer, intent(in) :: iopt,m,ntest,npest,maxit,ib1,ib3,nc,ncc,intest,nrest,lwrk integer, intent(out) :: ier integer, intent(inout) :: nt,np real(RKIND), intent(in) :: s,eta,tol real(RKIND), intent(inout) :: fp,sup ! ..array arguments.. real(RKIND), intent(in) :: teta(m),phi(m),r(m),w(m) real(RKIND), intent(inout) :: tt(ntest),tp(npest),c(nc),f(ncc),& row(npest),coco(npest),cosi(npest), & fpint(intest),coord(intest),ff(nc),bt(ntest,5),bp(npest,5),& a(ncc,ib1),q(ncc,ib3),spt(m,4),spp(m,4),h(ib3),wrk(lwrk) integer, intent(inout) :: index(nrest),nummer(m) ! ..local scalars.. real(RKIND) :: aa,acc,arg,cn,co,c1,dmax,d1,d2,eps,fac1,fac2,facc,facs,fn,fpmax,fpms,f1,f2,f3,htj, & p,pinv,piv,p1,p2,p3,ri,si,sigma,sq,store,wi,rn integer :: i,iband,iband1,iband3,iband4,ii,ij,il,in,irot,iter,i1,i2,j,jlt,jrot,j1,j2,l,la,lf,lh,ll,lp,& lt,lwest,l1,l2,l4,ncof,ncoff,npp,np4,nreg,nrint,nrr,nr1,ntt,nt4,nt6,num,num1,rank logical :: check1,check3,success ! ..local arrays.. real(RKIND), dimension(MAX_ORDER+1) :: hp,ht eps = sqrt(eta) ! Initializations lwest = 0 ntt = 0 iband1 = 0 bootstrap: if (iopt>=0) then ! calculation of acc, the absolute tolerance for the root of f(p)=s. acc = tol*s ! if iopt=0 we begin by computing the weighted least-squares polynomial of the form ! s(teta,phi) = c1*f1(teta) + cn*fn(teta) ! where f1(teta) and fn(teta) are the cubic polynomials satisfying ! f1(0) = 1, f1(pi) = f1'(0) = f1'(pi) = 0 ; fn(teta) = 1-f1(teta). ! the corresponding weighted sum of squared residuals gives the upper ! bound sup for the smoothing factor s. if (iopt==0 .or. (iopt>0 .and. s>=sup)) then sup = zero d1 = zero d2 = zero c1 = zero cn = zero aa = zero fac1 = pi*(one + half) fac2 = two/pi**3 initial_poly: do i=1,m wi = w(i) ri = r(i)*wi arg = teta(i) fn = fac2*(fac1-arg)*arg**2 f1 = (one-fn)*wi fn = fn*wi if (not_equal(fn,zero)) then call fpgivs(fn,d1,co,si) call fprota(co,si,f1,aa) call fprota(co,si,ri,cn) endif if (not_equal(f1,zero)) then call fpgivs(f1,d2,co,si) call fprota(co,si,ri,c1) endif sup = sup+ri*ri end do initial_poly if (not_equal(d2,zero)) c1 = c1/d2 if (not_equal(d1,zero)) cn = (cn-aa*c1)/d1 ! find the b-spline representation of this least-squares polynomial nt = 8 np = 8 c(1:8) = c1 c(9:16) = cn tt(1:4) = zero tt(5:8) = pi tp(1:4) = zero tp(5:8) = pi2 fp = sup ! test whether the least-squares polynomial is an acceptable solution fpms = sup-s if (fpms<acc) then ier = FITPACK_LEASTSQUARES_OK return end if endif ! test whether we cannot further increase the number of knots. sixty: if (iopt==0 .or. (iopt>0 .and. (s>=sup .or. np<11))) then if (npest<11 .or. ntest<9) then ier = FITPACK_INSUFFICIENT_STORAGE return end if ! find the initial set of interior knots of the spherical spline in case iopt = 0. np = 11 tp(5:7) = pi*[half,one,onep5] nt = 9 tt(5) = tp(5) endif sixty endif bootstrap ! ************************************************************************************************************ ! part 1 : computation of least-squares spherical splines. ! ************************************************************************************************************ ! if iopt1<0 we compute the least-squares spherical spline according to the given set of knots. ! if iopt1>=0 we compute least-squares spherical splines with increasing numbers of knots until the ! corresponding sum f(p=inf)<=s. ! the initial set of knots then depends on the value of iopt1: ! if iopt1=0 we start with one interior knot in the teta-direction (pi/2) and and three in the ! phi-direction (pi/2,pi,3*pi/2). ! if iopt1>0 we start with the set of knots found at the last call of the routine. ! ************************************************************************************************************ ! main loop for the different sets of knots. m is a safe upper bound for the number of trials. compute_knots: do iter=1,m ! find the position of the additional knots which are needed for the ! b-spline representation of s(teta,phi). tp(1:4) = [tp(np-6:np-4)-pi2, zero] tp(np-3:np) = [pi2, tp(5:7)+pi2] tt(1:4) = zero tt(nt-3:nt) = pi ! find nrint, the total number of knot intervals and nreg, the number ! of panels in which the approximation domain is subdivided by the ! intersection of knots. ntt = nt-7 npp = np-7 nrr = npp/2 nr1 = nrr+1 nrint = ntt+npp nreg = ntt*npp ! arrange the data points according to the panel they belong to. call fporde(teta,phi,m,3,3,tt,nt,tp,np,nummer,index,nreg) ! find the b-spline coefficients coco and cosi of the cubic spline ! approximations sc(phi) and ss(phi) for cos(phi) and sin(phi). coco(1:npp) = zero cosi(1:npp) = zero a(1:npp,1:npp) = zero ! the coefficients coco and cosi are obtained from the conditions ! sc(tp(i))=cos(tp(i)),resp. ss(tp(i))=sin(tp(i)),i=4,5,...np-4. get_coefs: do i=1,npp l2 = i+3 arg = tp(l2) hp = fpbspl(tp,np,3,arg,l2) row(1:npp) = zero ll = i do j=1,3 if (ll>npp) ll = 1 row(ll) = row(ll)+hp(j) ll = ll+1 end do facc = cos(arg) facs = sin(arg) do j=1,npp piv = row(j) if (equal(piv,zero)) cycle call fpgivs(piv,a(j,1),co,si) call fprota(co,si,facc,coco(j)) call fprota(co,si,facs,cosi(j)) if (j<npp) then j1 = j+1 i2 = 1 do l=j1,npp i2 = i2+1 call fprota(co,si,row(l),a(j,i2)) end do endif end do end do get_coefs coco(:npp) = fpback(a,coco,npp,npp,ncc) cosi(:npp) = fpback(a,cosi,npp,npp,ncc) ! find ncof, the dimension of the spherical spline and ncoff, the ! number of coefficients in the standard b-spline representation. nt4 = nt-4 np4 = np-4 ncoff = nt4*np4 ncof = 6+npp*(ntt-1) ! find the bandwidth of the observation matrix a. if (ntt<4) then iband = ncof elseif (ntt==4) then iband = 3*(npp+1) else iband = 4*npp end if iband1 = iband-1 ! initialize the observation matrix a. f(1:ncof) = zero a(1:ncof,1:iband) = zero ! initialize the sum of squared residuals. fp = zero ! fetch the data points in the new order. main loop for the different panels. panels: do num=1,nreg ! fix certain constants for the current panel; jrot records the column ! number of the first non-zero element in a row of the observation ! matrix according to a data point of the panel. num1 = num-1 lt = num1/npp l1 = lt+4 lp = num1-lt*npp+1 l2 = lp+3 lt = lt+1 jrot = merge(3+(lt-3)*npp,0,lt>2) ! test whether there are still data points in the current panel. in = index(num) points_left: do while (in/=0) ! fetch a new data point. wi = w(in) ri = r(in)*wi ! evaluate for the teta-direction, the 4 non-zero b-splines at teta(in) ht = fpbspl(tt,nt,3,teta(in),l1) ! evaluate for the phi-direction, the 4 non-zero b-splines at phi(in) hp = fpbspl(tp,np,3,phi(in),l2) ! store the value of these b-splines in spt and spp resp. spp(in,1:4) = hp(1:4) spt(in,1:4) = ht(1:4) ! initialize the new row of observation matrix. h(1:iband) = zero ! calculate the non-zero elements of the new row by making the cross ! products of the non-zero b-splines in teta- and phi-direction and ! by taking into account the conditions of the spherical splines. row(1:npp) = zero ! take into account the condition (3) of the spherical splines. ll = lp do i=1,4 if (ll>npp) ll=1 row(ll) = row(ll)+hp(i) ll = ll+1 end do ! take into account the other conditions of the spherical splines. if (lt<=2 .or. lt>=(ntt-1)) then facc = dot_product(row(:npp),coco(:npp)) facs = dot_product(row(:npp),cosi(:npp)) else facc = zero facs = zero endif ! fill in the non-zero elements of the new row. j1 = 0 new_row: do j =1,4 jlt = j+lt htj = ht(j) if (jlt==3) then h(1:3) = [h(1)+htj,facc*htj,facs*htj] j1 = 3 elseif (jlt==nt4) then h(j1+1:j1+3) = htj*[facc,facs,one] j1 = j1+2 elseif (jlt>2 .and. jlt<=nt4) then h(j1+1:j1+npp) = row(1:npp)*htj j1 = j1+npp else j1 = j1+1 h(j1) = h(j1)+htj endif end do new_row h(:iband) = h(:iband)*wi ! rotate the row into triangle by givens transformations. irot = jrot rotate: do i=1,iband irot = irot+1 piv = h(i) if (equal(piv,zero)) cycle rotate ! calculate the parameters of the givens transformation. call fpgivs(piv,a(irot,1),co,si) ! apply that transformation to the right hand side. call fprota(co,si,ri,f(irot)) ! apply that transformation to the left hand side. if (i<iband) call fprota(co,si,h(i+1:iband),a(irot,2:1+iband-i)) end do rotate ! add the contribution of the row to the sum of squares of residual ! right hand sides. fp = fp+ri**2 ! find the number of the next data point in the panel. in = nummer(in) end do points_left end do panels ! find dmax, the maximum value for the diagonal elements in the reduced triangle. dmax = max(zero,maxval(a(:ncof,1))) ! check whether the observation matrix is rank deficient. sigma = eps*dmax if (any(a(1:ncof,1)<=sigma)) then ! in case of rank deficiency, find the minimum norm solution. lwest = ncof*iband+ncof+iband if (lwest>lwrk) then ier = lwest return end if lf = 1 lh = lf+ncof la = lh+iband ff(1:ncof) = zero q(1:ncof,1:iband) = a(1:ncof,1:iband) call fprank(q,ff,ncof,iband,ncc,sigma,c,sq,rank,wrk(la),wrk(lf),wrk(lh)) q(1:ncof,1) = q(1:ncof,1)/dmax ! add to the sum of squared residuals, the contribution of reducing the rank. fp = fp+sq else ! backward substitution in case of full rank. c(:ncof) = fpback(a,f,ncof,iband,ncc) rank = ncof q(1:ncof,1) = a(1:ncof,1)/dmax endif ! find the coefficients in the standard b-spline representation of the spherical spline. call fprpsp(nt,np,coco,cosi,c,ff,ncoff) ! test whether the least-squares spline is an acceptable solution. fpms = fp-s if (iopt<0 .or. abs(fpms)<=acc) then if (fp<=zero) then ier = FITPACK_INTERPOLATING_OK fp = zero else if(ncof/=rank) ier = -rank endif return endif ! if f(p=inf) < s, accept the choice of knots. if (fpms<zero) exit compute_knots ! test whether we cannot further increase the number of knots. if (m<ncof) then ier = FITPACK_TOO_MANY_KNOTS return end if ! search where to add a new knot. ! find for each interval the sum of squared residuals fpint for the data points having the ! coordinate belonging to that knot interval. calculate also coord which is the same sum, ! weighted by the position of the data points considered. fpint(:nrint) = zero coord(:nrint) = zero do num=1,nreg num1 = num-1 lt = num1/npp l1 = lt+1 lp = num1-lt*npp l2 = lp+1+ntt jrot = lt*np4+lp in = index(num) do while (in/=0) store = zero i1 = jrot do i=1,4 store = store + spt(in,i)*dot_product(spp(in,1:4),c(i1+1:i1+4)) i1 = i1+np4 end do store = (w(in)*(r(in)-store))**2 fpint(l1) = fpint(l1)+store coord(l1) = coord(l1)+store*teta(in) fpint(l2) = fpint(l2)+store coord(l2) = coord(l2)+store*phi(in) in = nummer(in) end do end do ! find the interval for which fpint is maximal on the condition that ! there still can be added a knot. l1 = merge(ntt+1, 1,ntest<nt+1) l2 = merge(ntt ,nrint,npest<np+2) ! test whether we cannot further increase the number of knots. if (l1>l2) then ier = FITPACK_INSUFFICIENT_STORAGE return end if add_knot: do i = maxloc(fpint(l1:l2),1,fpint(l1:l2)>zero) l = l1+i-1 if (i==0) then ier = FITPACK_OVERLAPPING_KNOTS return end if fpmax = fpint(l) ! calculate the position of the new knot. arg = coord(l)/fpint(l) ! test in what direction the new knot is going to be added. choose_direction: if (l<=ntt) then ! addition in the teta-direction l4 = l+4 fpint(l) = zero fac1 = tt(l4)-arg fac2 = arg-tt(l4-1) else choose_direction ! addition in the phi-direction l4 = l+4-ntt if (arg>=pi) then arg = arg-pi l4 = l4-nrr endif fpint(l) = zero fac1 = tp(l4)-arg fac2 = arg-tp(l4-1) endif choose_direction ! Suitable location if (fac1<=(ten*fac2) .and. fac2<=(ten*fac1)) then ! Place knot place_knot: if (l<=ntt) then ! Place in teta coordinate j = nt do i=l4,nt tt(j+1) = tt(j) j = j-1 end do tt(l4) = arg nt = nt+1 else place_knot ! Place in phi coordinate ll = nrr+4 j = ll do i=l4,ll tp(j+1) = tp(j) j = j-1 end do tp(l4) = arg np = np+2 nrr = nrr+1 do i=5,ll j = i+nrr tp(j) = tp(i)+pi end do endif place_knot exit add_knot end if end do add_knot ! restart the computations with the new set of knots. end do compute_knots ! ************************************************************************************************************ ! part 2: determination of the smoothing spherical spline. ! ************************************************************************************************************ ! we have determined the number of knots and their position. we now compute the coefficients of the ! smoothing spline sp(teta,phi). the observation matrix a is extended by the rows of a matrix, expres- ! sing that sp(teta,phi) must be a constant function in the variable phi and a cubic polynomial in the ! variable teta. the corresponding weights of these additional rows are set to 1/(p). iteratively we then have ! to determine the value of p such that f(p) = sum((w(i)*(z(i)-sp(teta(i),phi(i))))**2) be = s. ! we already know that the least-squares polynomial corresponds to p=0, and that the least-squares ! spherical spline corresponds to p=infinity. the iteration process makes use of rational interpolation. ! since f(p) is a convex and strictly decreasing function of p, it can be approximated by a rational ! function of the form r(p) = (u*p+v)/(p+w). three values of p (p1,p2,p3) with corresponding values of ! f(p) (f1=f(p1)-s,f2=f(p2)-s,f3=f(p3)-s) are used to calculate the new value of p such that r(p)=s. ! convergence is guaranteed by taking f1>0,f3<zero ! ************************************************************************************************************ ! evaluate the discontinuity jumps of the 3-th order derivative of the b-splines at the knots call fpdisc(tt,nt,5,bt,ntest) ! tt(l),l=5,...,nt-4. call fpdisc(tp,np,5,bp,npest) ! tp(l),l=5,...,np-4. ! initial value for p. p1 = zero f1 = sup-s p3 = -one f3 = fpms p = sum(a(1:ncof,1)) rn = ncof p = rn/p ! find the bandwidth of the extended observation matrix. iband4 = merge(ncof,iband+3,ntt<=4) iband3 = iband4 -1 check1 = .false. check3 = .false. ! iteration process to find the root of f(p)=s. iterations: do iter=1,maxit pinv = one/p ! store the triangularized observation matrix into q. ff(1:ncof) = f(1:ncof) q(1:ncof,1:iband4) = zero q(1:ncof,1:iband) = a(1:ncof,1:iband) ! extend the observation matrix with the rows of a matrix, expressing ! that for teta=cst. sp(teta,phi) must be a constant function. nt6 = nt-6 const_teta: do i=5,np4 ii = i-4 row(1:npp) = zero ll = ii do l=1,5 if (ll>npp) ll=1 row(ll) = row(ll)+bp(ii,l) ll = ll+1 end do facc = dot_product(row(1:npp),coco(1:npp)) facs = dot_product(row(1:npp),cosi(1:npp)) do j=1,nt6 ! initialize the new row. h(1:iband) = zero ! fill in the non-zero elements of the row. jrot records the column ! number of the first non-zero element in the row. jrot = 4+(j-2)*npp if (j>1 .and. j<nt6) then h(1:npp) = row(1:npp) else h(1:2) = [facc,facs] if(j==1) jrot = 2 endif h(1:iband) = h(1:iband)*pinv ri = zero ! rotate the new row into triangle by givens transformations. rot_new_row: do irot=jrot,ncof piv = h(1) i2 = min(iband1,ncof-irot) if (equal(piv,zero)) then if (i2<=0) exit rot_new_row else ! calculate the parameters of the givens transformation. call fpgivs(piv,q(irot,1),co,si) ! apply that givens transformation to the right hand side. call fprota(co,si,ri,ff(irot)) if (i2==0) exit rot_new_row ! apply that givens transformation to the left hand side. call fprota(co,si,h(2:i2+1),q(irot,2:i2+1)) endif h(1:i2+1) = [h(2:i2+1),zero] end do rot_new_row end do end do const_teta ! extend the observation matrix with the rows of a matrix expressing ! that for phi=cst. sp(teta,phi) must be a cubic polynomial. const_phi: do i=5,nt4 ii = i-4 do j=1,npp ! initialize the new row h(1:iband4) = zero ! fill in the non-zero elements of the row. jrot records the column ! number of the first non-zero element in the row. j1 = 1 fill_in: do l=1,5 il = ii+l ij = npp if (il==3 .or. il==nt4) then if (il==3) then j1 = 1 j2 = 2 ij = j+2 else j1 = j1+3-j j2 = j1-2 ij = 0 endif h(j2:j2+1) = bt(ii,l)*[coco(j),cosi(j)] endif h(j1) = h(j1)+bt(ii,l) j1 = j1+ij end do fill_in h(:iband4) = pinv*h(:iband4) ri = zero jrot = merge(3+j+(ii-3)*npp,1,ii>2) ! rotate the new row into triangle by givens transformations. rot_new_phi: do irot=jrot,ncof piv = h(1) i2 = min(iband3,ncof-irot) if (equal(piv,zero)) then if (i2<=0) exit rot_new_phi else ! calculate the parameters of the givens transformation. call fpgivs(piv,q(irot,1),co,si) ! apply that givens transformation to the right hand side. call fprota(co,si,ri,ff(irot)) if (i2==0) exit rot_new_phi ! apply that givens transformation to the left hand side. call fprota(co,si,h(2:i2+1),q(irot,2:i2+1)) endif h(1:i2+1) = [h(2:i2+1),zero] end do rot_new_phi end do end do const_phi ! find dmax, the maximum value for the diagonal elements in the reduced triangle. dmax = max(zero,maxval(q(1:ncof,1))) ! check whether the matrix is rank deficient. sigma = eps*dmax if (all(q(1:ncof,1)>sigma)) then ! backward substitution in case of full rank. c(:ncof) = fpback(q,ff,ncof,iband4,ncc) rank = ncof else ! In case of rank deficiency, find the minimum norm solution lwest = ncof*iband4+ncof+iband4 if (lwest>lwrk) then ier = lwest return end if lf = 1 lh = lf+ncof la = lh+iband4 call fprank(q,ff,ncof,iband4,ncc,sigma,c,sq,rank,wrk(la),wrk(lf),wrk(lh)) end if q(1:ncof,1) = q(1:ncof,1)/dmax ! find the coefficients in the standard b-spline representation of the spherical spline. call fprpsp(nt,np,coco,cosi,c,ff,ncoff) ! compute f(p). fp = zero get_fp: do num = 1,nreg num1 = num-1 lt = num1/npp lp = num1-lt*npp jrot = lt*np4+lp in = index(num) do while (in/=0) store = zero i1 = jrot do i=1,4 store = store+spt(in,i)*dot_product(spp(in,1:4),c(i1+1:i1+4)) i1 = i1+np4 end do fp = fp+(w(in)*(r(in)-store))**2 in = nummer(in) end do end do get_fp ! test whether the approximation sp(teta,phi) is an acceptable solution fpms = fp-s if (abs(fpms)<=acc) then if (ncof/=rank) ier = -rank return end if ! find the new value of p and carry out one more step. call root_finding_iterate(p1,f1,p2,f2,p3,f3,p,fpms,acc,check1,check3,success) if (.not.success) then ier = FITPACK_S_TOO_SMALL return end if end do iterations ! the maximum allowable number of iterations has been reached. ier = FITPACK_MAXIT return end subroutine fpsphe ! Once all inputs checked, do the actual b-spline surface evaluation pure subroutine fpsuev(idim,tu,nu,tv,nv,c,u,mu,v,mv,f,wu,wv,lu,lv) ! ..scalar arguments.. integer, intent(in) :: idim,nu,nv,mu,mv ! ..array arguments.. integer, intent(out) :: lu(mu),lv(mv) real(RKIND), intent(in) :: tu(nu),tv(nv),c((nu-4)*(nv-4)*idim),u(mu),v(mv) real(RKIND), intent(out) :: wu(mu,4),wv(mv,4),f(mu*mv*idim) ! ..local scalars.. integer :: i,i1,j,j1,k,l,l1,l3,m,nuv,nu4,nv4 real(RKIND) :: arg,sp,tb,te ! ..local arrays.. real(RKIND) :: h(MAX_ORDER+1) ! Process u nu4 = nu-4 tb = tu(4) te = tu(nu4+1) l = 4 l1 = l+1 do i=1,mu arg = min(max(tb,u(i)),te) do while (.not.(arg<tu(l1) .or. l==nu4)) l = l1 l1 = l+1 end do h = fpbspl(tu,nu,3,arg,l) lu(i) = l-4 wu(i,1:4) = h(1:4) end do ! Process v nv4 = nv-4 tb = tv(4) te = tv(nv4+1) l = 4 l1 = l+1 do i=1,mv arg = min(max(v(i),tb),te) do while (.not.(arg<tv(l1) .or. l==nv4)) l = l1 l1 = l+1 end do h = fpbspl(tv,nv,3,arg,l) lv(i) = l-4 wv(i,1:4) = h(1:4) end do m = 0 nuv = nu4*nv4 dims: do k=1,idim l3 = (k-1)*nuv do i=1,mu l = lu(i)*nv4+l3 h(1:4) = wu(i,1:4) do j=1,mv l1 = l+lv(j) sp = zero do i1=1,4 do j1=1,4 sp = sp+c(l1+j1)*h(i1)*wv(j,j1) end do l1 = l1+nv4 end do m = m+1 f(m) = sp end do end do end do dims return end subroutine fpsuev pure subroutine fpsurf(iopt,m,x,y,z,w,xb,xe,yb,ye,kxx,kyy, & s,nxest, nyest,eta,tol,maxit,nmax,km1,km2,ib1,ib3,nc,intest, & nrest,nx0,tx,ny0,ty,c,fp,fp0,fpint,coord,f,ff,a,q,bx,by,spx, & spy,h,index,nummer,wrk,lwrk,ier) ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: xb,xe,yb,ye,s,eta,tol real(RKIND), intent(inout) :: fp,fp0 integer, intent(in) :: iopt,m,kxx,kyy,nxest,nyest,maxit,nmax,km1,km2,ib1,ib3, & nc,intest,nrest,lwrk integer, intent(inout) :: nx0,ny0,ier ! ..array arguments.. real(RKIND), intent(in) :: z(m),w(m) real(RKIND), intent(inout) :: x(m),y(m),tx(nmax),ty(nmax),c(nc),fpint(intest),coord(intest),& f(nc),ff(nc),a(nc,ib1),q(nc,ib3),bx(nmax,km2),by(nmax,km2), & spx(m,km1),spy(m,km1),h(ib3),wrk(lwrk) integer, intent(inout) :: index(nrest),nummer(m) ! ..local scalars.. real(RKIND) :: acc,arg,cos,dmax,fac1,fac2,fpmax,fpms,f1,f2,f3,hxi,p,pinv,piv,p1,p2,p3,sigma,& sin,sq,store,wi,x0,x1,y0,y1,zi,eps integer :: i,iband,iband1,iband3,iband4,ii,in,irot,iter,i1,i2,j,jrot,& jxy,j1,kx,kx1,kx2,ky,ky1,ky2,l,la,lf,lh,lwest,lx,ly,l1,l2,n,ncof,nk1x,nk1y,nminx,& nminy,nreg,nrint,num,num1,nx,nxe,nxx,ny,nye,nyy,rank logical :: interchanged,check1,check3,success ! ..local arrays.. real(RKIND), dimension(MAX_ORDER+1) :: hx,hy ! ********************************************************************************************** ! part 1: determination of the number of knots and their position ! ********************************************************************************************** ! given a set of knots we compute the least-squares spline sinf(x,y), and the corresponding ! weighted sum of squared residuals fp=f(p=inf). ! if iopt=-1 sinf(x,y) is the requested approximation. ! if iopt=0 or iopt=1 we check whether we can accept the knots: ! if fp <=s we will continue with the current set of knots. ! if fp > s we will increase the number of knots and compute the orresponding least-squares ! spline until finally fp<=s. ! the initial choice of knots depends on the value of s and iopt. ! if iopt=0 we first compute the least-squares polynomial of degree kx in x and ky in y; ! nx=nminx=2*kx+2 and ny=nminy=2*ky+2. ! fp0=f(0) denotes the corresponding weighted sum of squared residuals ! if iopt=1 we start with the set of knots found at the last call of the routine, except for ! the case that s >=fp0; then we compute the least-squares polynomial directly. ! eventually the independent variables x and y (and the corresponding parameters) will be ! switched if this can reduce the bandwidth of the system to be solved. ! ********************************************************************************************** ! interchanged denotes whether or not the directions have been inter changed. interchanged = .false. x0 = xb x1 = xe y0 = yb y1 = ye kx = kxx ky = kyy kx1 = kx+1 ky1 = ky+1 nxe = nxest nye = nyest eps = sqrt(eta) bootstrap: if (iopt>=0) then ! calculation of acc, the absolute tolerance for the root of f(p)=s. acc = tol*s if (iopt==0 .or. fp0<=s) then ! initialization for the least-squares polynomial. nminx = 2*kx1 nminy = 2*ky1 nx = nminx ny = nminy ier = FITPACK_LEASTSQUARES_OK else nx = nx0 ny = ny0 end if else bootstrap nx = nx0 ny = ny0 endif bootstrap ! main loop for the different sets of knots. m is a safe upper bound for the number of trials. compute_knots: do iter=1,m ! find the position of the additional knots which are needed for the ! b-spline representation of s(x,y). tx(1:kx1) = x0 tx(nx-kx:nx) = x1 ty(1:ky1) = y0 ty(ny-ky:ny) = y1 ! find nrint, the total number of knot intervals and nreg, the number ! of panels in which the approximation domain is subdivided by the ! intersection of knots. nxx = nx-2*kx1+1 nyy = ny-2*ky1+1 nrint = nxx+nyy nreg = nxx*nyy ! find the bandwidth of the observation matrix a. ! if necessary, interchange the variables x and y, in order to obtain a minimal bandwidth. iband1 = kx*(ny-ky1)+ky l = ky*(nx-kx1)+kx do_interchange: if (iband1>l) then iband1 = l interchanged = .not.interchanged call swap_RKIND(x,y) call swap_RKIND(x0,y0) call swap_RKIND(x1,y1) n = min(nx,ny) call swap_RKIND(tx ,ty) call swap_int (nx ,ny) call swap_int (nxe,nye) call swap_int (nxx,nyy) call swap_int (kx ,ky) kx1 = kx+1 ky1 = ky+1 endif do_interchange iband = iband1+1 ! arrange the data points according to the panel they belong to. call fporde(x,y,m,kx,ky,tx,nx,ty,ny,nummer,index,nreg) ! find ncof, the number of b-spline coefficients. nk1x = nx-kx1 nk1y = ny-ky1 ncof = nk1x*nk1y ! initialize the observation matrix a. f(1:ncof) = zero a(1:ncof,1:iband) = zero ! initialize the sum of squared residuals. fp = zero ! fetch the data points in the new order. main loop for the ! different panels. panels: do num=1,nreg ! fix certain constants for the current panel; jrot records the column ! number of the first non-zero element in a row of the observation ! matrix according to a data point of the panel. num1 = num-1 lx = num1/nyy l1 = lx+kx1 ly = num1-lx*nyy l2 = ly+ky1 jrot = lx*nk1y+ly ! test whether there are still data points in the panel. in = index(num) points_left: do while (in/=0) ! fetch a new data point. wi = w(in) zi = z(in)*wi ! evaluate for the x-direction, the (kx+1) non-zero b-splines at x(in). hx = fpbspl(tx,nx,kx,x(in),l1) ! evaluate for the y-direction, the (ky+1) non-zero b-splines at y(in). hy = fpbspl(ty,ny,ky,y(in),l2) ! store the value of these b-splines in spx and spy respectively. spx(in,1:kx1) = hx(1:kx1) spy(in,1:ky1) = hy(1:ky1) ! initialize the new row of observation matrix. h(1:iband) = zero ! calculate the non-zero elements of the new row by making the cross ! products of the non-zero b-splines in x- and y-direction. i1 = 0 do i=1,kx1 hxi = hx(i) j1 = i1 do j=1,ky1 j1 = j1+1 h(j1) = hxi*hy(j)*wi end do i1 = i1+nk1y end do ! rotate the row into triangle by givens transformations . irot = jrot rotate: do i=1,iband irot = irot+1 piv = h(i) if (equal(piv,zero)) cycle rotate ! calculate the parameters of the givens transformation. call fpgivs(piv,a(irot,1),cos,sin) ! apply that transformation to the right hand side. call fprota(cos,sin,zi,f(irot)) ! apply that transformation to the left hand side. if (i<iband) call fprota(cos,sin,h(i+1:iband),a(irot,2:1+iband-i)) end do rotate ! add the contribution of the row to the sum of squares of residual right hand sides. fp = fp+zi**2 ! find the number of the next data point in the panel. in = nummer(in) end do points_left end do panels ! find dmax, the maximum value for the diagonal elements in the reduced triangle. dmax = max(zero,maxval(a(1:ncof,1))) ! check whether the observation matrix is rank deficient. sigma = eps*dmax if (all(a(1:ncof,1)>sigma)) then ! backward substitution in case of full rank. c(:ncof) = fpback(a,f,ncof,iband,nc) rank = ncof q(:ncof,1) = a(:ncof,1)/dmax else ! in case of rank deficiency, find the minimum norm solution. ! check whether there is sufficient working space lwest = ncof*iband+ncof+iband if (lwest>lwrk) then ier = lwest call sort_xy(interchanged,m,nmax,nc,nk1x,nk1y,iopt,l1,l2,c,f,x,y,tx,ty,nx,ny,nx0,ny0) return end if ff(1:ncof) = f(1:ncof) q(1:ncof,1:iband)=a(1:ncof,1:iband) lf = 1 lh = lf+ncof la = lh+iband call fprank(q,ff,ncof,iband,nc,sigma,c,sq,rank,wrk(la),wrk(lf),wrk(lh)) q(1:ncof,1)=q(1:ncof,1)/dmax ! add to the sum of squared residuals, the contribution of reducing the rank. fp = fp+sq endif if (ier==FITPACK_LEASTSQUARES_OK) fp0 = fp ! test whether the least-squares spline is an acceptable solution. fpms = fp-s if (iopt<0 .or. abs(fpms)<=acc) then if (fp<=zero) then ier = FITPACK_INTERPOLATING_OK fp = zero endif if (ncof/=rank) ier = -rank call sort_xy(interchanged,m,nmax,nc,nk1x,nk1y,iopt,l1,l2,c,f,x,y,tx,ty,nx,ny,nx0,ny0) return endif ! test whether we can accept the choice of knots. if (fpms<zero) exit compute_knots ! test whether we cannot further increase the number of knots. if (m<ncof) then ier = FITPACK_TOO_MANY_KNOTS call sort_xy(interchanged,m,nmax,nc,nk1x,nk1y,iopt,l1,l2,c,f,x,y,tx,ty,nx,ny,nx0,ny0) return endif ier = FITPACK_OK ! search where to add a new knot. ! find for each interval the sum of squared residuals fpint for the ! data points having the coordinate belonging to that knot interval. ! calculate also coord which is the same sum, weighted by the position ! of the data points considered. fpint(:nrint) = zero coord(:nrint) = zero do num=1,nreg num1 = num-1 lx = num1/nyy l1 = lx+1 ly = num1-lx*nyy l2 = ly+1+nxx jrot = lx*nk1y+ly in = index(num) do while (in/=0) store = zero i1 = jrot do i=1,kx1 store = store+spx(in,i)*dot_product(spy(in,1:ky1),c(i1+1:i1+ky1)) i1 = i1+nk1y end do store = (w(in)*(z(in)-store))**2 fpint(l1) = fpint(l1)+store coord(l1) = coord(l1)+store*x(in) fpint(l2) = fpint(l2)+store coord(l2) = coord(l2)+store*y(in) in = nummer(in) end do end do ! find the interval for which fpint is maximal on the condition that ! there still can be added a knot. add_knot: do l = 0 fpmax = zero l1 = merge(nxx+1, 1,nx==nxe) l2 = merge(nxx ,nrint,ny==nye) if (l1>l2) then ier = FITPACK_INSUFFICIENT_STORAGE call sort_xy(interchanged,m,nmax,nc,nk1x,nk1y,iopt,l1,l2,c,f,x,y,tx,ty,nx,ny,nx0,ny0) return endif l = 0 do i=l1,l2 if (fpmax<fpint(i)) then l = i fpmax = fpint(i) endif end do ! test whether we cannot further increase the number of knots. if (l==0) then ier = FITPACK_OVERLAPPING_KNOTS call sort_xy(interchanged,m,nmax,nc,nk1x,nk1y,iopt,l1,l2,c,f,x,y,tx,ty,nx,ny,nx0,ny0) return end if ! calculate the position of the new knot. arg = coord(l)/fpint(l) ! test in what direction the new knot is going to be added. choose_direction: if (l<=nxx) then ! addition in the x-direction. jxy = l+kx1 fpint(l) = zero fac1 = tx(jxy)-arg fac2 = arg-tx(jxy-1) else choose_direction ! addition in the y-direction. jxy = l+ky1-nxx fpint(l) = zero fac1 = ty(jxy)-arg fac2 = arg-ty(jxy-1) endif choose_direction ! Suitable location if (fac1<=(ten*fac2) .and. fac2<=(ten*fac1)) then ! Place knot place_knot: if (l<=nxx) then j = nx do i=jxy,nx tx(j+1) = tx(j) j = j-1 end do tx(jxy) = arg nx = nx+1 else place_knot j = ny do i=jxy,ny ty(j+1) = ty(j) j = j-1 end do ty(jxy) = arg ny = ny+1 end if place_knot exit add_knot end if end do add_knot ! restart the computations with the new set of knots. end do compute_knots ! test whether the least-squares polynomial is a solution of our approximation problem. if (ier==FITPACK_LEASTSQUARES_OK) then call sort_xy(interchanged,m,nmax,nc,nk1x,nk1y,iopt,l1,l2,c,f,x,y,tx,ty,nx,ny,nx0,ny0) return endif ! ********************************************************************************************** ! part 2: determination of the smoothing spline sp(x,y) ! ********************************************************************************************** ! we have determined the number of knots and their position. we now compute the b-spline ! coefficients of the smoothing spline sp(x,y). the observation matrix a is extended by the ! rows of a matrix, expressing that sp(x,y) must be a polynomial of degree kx in x and ky in y. ! the corresponding weights of these additional rows are set to 1./p. iteratively we than have ! to determine the value of p such that f(p)=sum((w(i)*(z(i)-sp(x(i),y(i))))**2) be = s. ! we already know that the least-squares polynomial corresponds to p=0 and that the least-squares ! spline corresponds to p=infinity. the iteration process which is proposed here makes use of ! rational interpolation. since f(p) is a convex and strictly decreasing function of p, it can be ! approximated by a rational function r(p)=(u*p+v)/(p+w). three values of p(p1,p2,p3) with ! corresponding values of f(p) (f1=f(p1)-s,f2=f(p2)-s,f3=f(p3)-s) are used to calculate the ! new value of p such that r(p)=s. convergence is guaranteed by taking f1 > 0 and f3 < 0. c ! ********************************************************************************************** kx2 = kx1+1 ky2 = ky1+1 ! if there are interior knots in the x-direction, ! evaluate the discotinuity jumps of the kx-th order derivative of ! the b-splines at the knots tx(l),l=kx+2,...,nx-kx-1. if (nk1x/=kx1) call fpdisc(tx,nx,kx2,bx,nmax) ! if there are interior knots in the y-direction, ! evaluate the discontinuity jumps of the ky-th order derivative of ! the b-splines at the knots ty(l),l=ky+2,...,ny-ky-1. if (nk1y/=ky1) call fpdisc(ty,ny,ky2,by,nmax) ! initial value for p. p1 = zero f1 = fp0-s p3 = -one f3 = fpms p = sum(a(:ncof,1))/ncof ! find the bandwidth of the extended observation matrix. iband3 = kx1*nk1y iband4 = iband3 +1 check1 = .false. check3 = .false. ! iteration process to find the root of f(p)=s. root_iterations: do iter=1,maxit pinv = one/p ! store the triangularized observation matrix into q. ff(1:ncof) = f(1:ncof) q(1:ncof,1:iband) = a(1:ncof,1:iband) q(1:ncof,iband+1:iband4) = zero constant_x: if (nk1y/=ky1) then ! extend the observation matrix with the rows of a matrix, expressing ! that for x=cst. sp(x,y) must be a polynomial in y of degree ky. do i=ky2,nk1y ii = i-ky1 do j=1,nk1x ! initialize the new row. h(1:iband) = zero ! fill in the non-zero elements of the row. jrot records the column ! number of the first non-zero element in the row. h(1:ky2) = by(ii,1:ky2)*pinv zi = zero jrot = (j-1)*nk1y+ii ! rotate the new row into triangle by givens transformations without square roots. rot_new_row: do irot=jrot,ncof piv = h(1) i2 = min(iband1,ncof-irot) if (equal(piv,zero)) then if (i2<=0) exit rot_new_row else ! calculate the parameters of the givens transformation. call fpgivs(piv,q(irot,1),cos,sin) ! apply that givens transformation to the right hand side. call fprota(cos,sin,zi,ff(irot)) if (i2==0) exit rot_new_row ! apply that givens transformation to the left hand side. call fprota(cos,sin,h(2:i2+1),q(irot,2:i2+1)) endif h(1:i2+1) = [h(2:i2+1),zero] end do rot_new_row end do end do end if constant_x constant_y: if (nk1x/=kx1) then ! extend the observation matrix with the rows of a matrix expressing ! that for y=cst. sp(x,y) must be a polynomial in x of degree kx. do i=kx2,nk1x ii = i-kx1 do j=1,nk1y ! initialize the new row h(1:iband4) = zero ! fill in the non-zero elements of the row. jrot records the column ! number of the first non-zero element in the row. j1 = 1 do l=1,kx2 h(j1) = bx(ii,l)*pinv j1 = j1+nk1y end do zi = zero jrot = (i-kx2)*nk1y+j ! rotate the new row into triangle by givens transformations . rot_new_rowy: do irot=jrot,ncof piv = h(1) i2 = min(iband3,ncof-irot) if (equal(piv,zero)) then if (i2<=0) exit rot_new_rowy else ! calculate the parameters of the givens transformation. call fpgivs(piv,q(irot,1),cos,sin) ! apply that givens transformation to the right hand side. call fprota(cos,sin,zi,ff(irot)) if (i2==0) exit rot_new_rowy ! apply that givens transformation to the left hand side. call fprota(cos,sin,h(2:i2+1),q(irot,2:i2+1)) endif h(1:i2+1) = [h(2:i2+1),zero] end do rot_new_rowy end do ! 631 end do endif constant_y ! find dmax, the maximum value for the diagonal elements in the reduced triangle. dmax = max(zero,maxval(q(1:ncof,1),1)) ! check whether the matrix is rank deficient. sigma = eps*dmax if (all(q(1:ncof,1)>sigma)) then ! backward substitution in case of full rank. c(:ncof) = fpback(q,ff,ncof,iband4,nc) rank = ncof else ! in case of rank deficiency, find the minimum norm solution. lwest = ncof*iband4+ncof+iband4 if (lwest>lwrk) then ier = lwest call sort_xy(interchanged,m,nmax,nc,nk1x,nk1y,iopt,l1,l2,c,f,x,y,tx,ty,nx,ny,nx0,ny0) return end if lf = 1 lh = lf+ncof la = lh+iband4 call fprank(q,ff,ncof,iband4,nc,sigma,c,sq,rank,wrk(la),wrk(lf),wrk(lh)) endif q(1:ncof,1) = q(1:ncof,1)/dmax ! compute f(p). fp = zero get_fp: do num = 1,nreg num1 = num-1 lx = num1/nyy ly = num1-lx*nyy jrot = lx*nk1y+ly in = index(num) do while (in/=0) store = zero i1 = jrot do i=1,kx1 store = store+spx(in,i)*dot_product(spy(in,1:ky1),c(i1+1:i1+ky1)) i1 = i1+nk1y end do fp = fp+(w(in)*(z(in)-store))**2 in = nummer(in) end do end do get_fp ! test whether the approximation sp(x,y) is an acceptable solution. fpms = fp-s if (abs(fpms)<=acc) then if (ncof/=rank) ier = -rank call sort_xy(interchanged,m,nmax,nc,nk1x,nk1y,iopt,l1,l2,c,f,x,y,tx,ty,nx,ny,nx0,ny0) return end if ! find the new value of p and carry out one more step. call root_finding_iterate(p1,f1,p2,f2,p3,f3,p,fpms,acc,check1,check3,success) if (.not.success) then ier = FITPACK_S_TOO_SMALL call sort_xy(interchanged,m,nmax,nc,nk1x,nk1y,iopt,l1,l2,c,f,x,y,tx,ty,nx,ny,nx0,ny0) return end if end do root_iterations ! the maximum allowable number of iterations has been reached. ier = FITPACK_MAXIT ! test whether x and y are in the original order. call sort_xy(interchanged,m,nmax,nc,nk1x,nk1y,iopt,l1,l2,c,f,x,y,tx,ty,nx,ny,nx0,ny0) return contains ! Ensure x,y are returned in the original order, if they were interchanged pure subroutine sort_xy(interchanged,m,nmax,nc,nk1x,nk1y,iopt,l1,l2,c,f,x,y,tx,ty,nx,ny,nx0,ny0) logical, intent(in) :: interchanged integer, intent(in) :: m,nmax,nc,iopt,nk1x,nk1y integer, intent(inout) :: l1,l2,nx,ny,nx0,ny0 real(RKIND), intent(inout) :: x(m),y(m),tx(nmax),ty(nmax),c(nc),f(nc) integer :: i,j,n,ncof restore_xy: if (interchanged) then ! if not, interchange x and y once more. ncof = nk1x*nk1y ! Sort c using f as a temporary array l1 = 1 do i=1,nk1x l2 = i do j=1,nk1y f(l2) = c(l1) l1 = l1+1 l2 = l2+nk1x end do end do c(1:ncof) = f(1:ncof) n = min(nx,ny) call swap_RKIND(x,y) call swap_RKIND(tx,ty) call swap_int (nx,ny) endif restore_xy if (iopt>=0) then nx0 = nx ny0 = ny endif end subroutine sort_xy end subroutine fpsurf ! subroutine fpsysy solves a linear n x n symmetric system (a) * (b) = (g), with n<=6 ! on input, vector g contains the right hand side ; on output it will contain the solution (b). pure subroutine fpsysy(a,n,g) ! ..scalar arguments.. integer, intent(in) :: n ! ..array arguments.. real(RKIND), intent(inout) :: a(6,6) real(RKIND), intent(inout) :: g(6) ! ..local scalars.. real(RKIND) :: fac integer :: i,i1,j,k ! .. g(1) = g(1)/a(1,1) if (n==1) return ! decomposition of the symmetric matrix (a) = (l) * (d) *(l)' with (l) a unit lower triangular ! matrix and (d) a diagonal matrix a(2:n,1) = a(2:n,1)/a(1,1) do i=2,n i1 = i-1 do k=i,n fac = a(k,i) do j=1,i1 fac = fac-a(j,j)*a(k,j)*a(i,j) end do a(k,i) = fac if (k>i) a(k,i) = fac/a(i,i) end do end do ! solve the system (l)*(d)*(l)'*(b) = (g). ! first step : solve (l)*(d)*(c) = (g). do i=2,n i1 = i-1 fac = g(i) do j=1,i1 fac = fac-g(j)*a(j,j)*a(i,j) end do g(i) = fac/a(i,i) end do ! second step : solve (l)'*(b) = (c) i = n do j=2,n i1 = i i = i-1 fac = g(i) do k=i1,n fac = fac-g(k)*a(k,i) end do g(i) = fac end do return end subroutine fpsysy ! subroutine fptrnp reduces the (m+n-7) x (n-4) matrix a to upper triangular form and applies the ! same givens transformations to the (m) x (mm) x (idim) matrix z to obtain the (n-4) x (mm) x ! (idim) matrix q pure subroutine fptrnp(m,mm,idim,n,nr,sp,p,b,z,a,q,right) ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: p integer, intent(in) :: m,mm,idim,n ! ..array arguments.. real(RKIND), intent(in) :: sp(m,4),b(n,5),z(m*mm*idim) real(RKIND), intent(out) :: q((n-4)*mm*idim),a(n,5),right(mm*idim) integer, intent(in) :: nr(m) ! ..local scalars.. real(RKIND) :: cos,pinv,piv,sin integer :: i,iband,irot,it,ii,i2,i3,j,jj,l,mid,nmd,m2,m3,nrold,n4,number,n1 ! ..local arrays.. real(RKIND) :: h(MAX_ORDER+1) ! ..subroutine references.. ! fpgivs,fprota ! .. pinv = merge(one/p,one,p>zero) n4 = n-4 mid = mm*idim m2 = m*mm m3 = n4*mm ! reduce the matrix (a) to upper triangular form (r) using givens ! rotations. apply the same transformations to the rows of matrix z ! to obtain the mm x (n-4) matrix g. ! store matrix (r) into (a) and g into q. ! initialization. nmd = n4*mid q(1:nmd) = zero a(1:n4,1:5) = zero ! iband denotes the bandwidth of the matrices (a) and (r). iband = 4 nrold = 0 number = nr(1) rows: do it=1,m number = nr(it) increase_old: do ! 150 if (nrold/=number) then if (p<=zero) then nrold = nrold+1 cycle increase_old end if iband = 5 ! fetch a new row of matrix (b). n1 = nrold+1 h(1:5) = b(n1,1:5)*pinv ! find the appropriate column of q. right(1:mid) = zero irot = nrold else ! fetch a new row of matrix (sp). h(iband) = zero h(1:4) = sp(it,1:4) ! find the appropriate column of q. j = 0 do ii=1,idim l = (ii-1)*m2+(it-1)*mm do jj=1,mm j = j+1 l = l+1 right(j) = z(l) end do end do irot = number endif ! rotate the new row of matrix (a) into triangle. rotate: do i=1,iband irot = irot+1 piv = h(i); if (equal(piv,zero)) cycle rotate ! calculate the parameters of the givens transformation. call fpgivs(piv,a(irot,1),cos,sin) ! apply that transformation to the rows of matrix q. j = 0 do ii=1,idim l = (ii-1)*m3+irot do jj=1,mm j = j+1 call fprota(cos,sin,right(j),q(l)) l = l+n4 end do end do ! apply that transformation to the columns of (a). if (i<iband) then i2 = 1 i3 = i+1 do j=i3,iband i2 = i2+1 call fprota(cos,sin,h(j),a(irot,i2)) end do endif end do rotate if (nrold==number) cycle rows nrold = nrold+1 end do increase_old end do rows return end subroutine fptrnp ! subroutine fptrpe reduces the (m+n-7) x (n-7) cyclic bandmatrix a to upper triangular form and ! applies the same givens transformations to the (m) x (mm) x (idim) matrix z to obtain the ! (n-7) x (mm) x (idim) matrix q. pure subroutine fptrpe(m,mm,idim,n,nr,sp,p,b,z,a,aa,q,right) ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: p integer, intent(in) :: m,mm,idim,n ! ..array arguments.. real(RKIND), intent(in) :: sp(m,4),b(n,5),z(m*mm*idim) real(RKIND), intent(out) :: q((n-7)*mm*idim),right(mm*idim),a(n,5),aa(n,4) integer, intent(in) :: nr(m) ! ..local scalars.. real(RKIND) :: co,pinv,piv,si integer :: i,irot,it,ii,i2,j,jj,l,mid,nmd,m2,m3,nrold,n4,number,n1,n7,n11,m1 integer :: ij,jk,jper,l0,l1,ik ! ..local arrays.. real(RKIND) :: h(5),h1(5),h2(4) ! .. pinv = merge(one/p,one,p>zero) n4 = n-4 n7 = n-7 n11 = n-11 mid = mm*idim m2 = m*mm m3 = n7*mm m1 = m-1 ! we determine the matrix (a) and then we reduce her to upper triangular form (r) using givens ! rotations. we apply the same transformations to the rows of matrix z to obtain the (mm) x (n-7) ! matrix g. we store matrix (r) into a and aa, g into q. the n7 x n7 upper triangular matrix (r) ! has the form ! | a1 ' | ! (r) = | ' a2 | ! | 0 ' | ! with (a2) a n7 x 4 matrix and (a1) a n11 x n11 upper triangular matrix of bandwidth 5. ! initialization. jper = 0 nrold = 0 nmd = n7*mid q (1:nmd) = zero aa(1:n4,:) = zero a (1:n4,:) = zero iterate: do it=1,m1 number = nr(it) inner: do if (nrold==number) then ! fetch a new row of matrix (sp) h = [sp(it,:),zero] ! find the appropriate row of q. j = 0 do ii=1,idim l = (ii-1)*m2+(it-1)*mm do jj=1,mm j = j+1 l = l+1 right(j) = z(l) end do end do elseif (p<=zero) then nrold = nrold+1 cycle inner else ! fetch a new row of matrix (b). n1 = nrold+1 h = b(n1,:)*pinv ! find the appropriate row of q. right(:mid) = zero endif ! test whether there are non-zero values in the new row of (a) ! corresponding to the b-splines n(j,*),j=n7+1,...,n4. if (nrold>=n11) then if (jper==0) then ! initialize the matrix (aa). jk = n11+1 do i=1,4 ik = jk do j=1,5 if (ik<=0) exit aa(ik,i) = a(ik,j) ik = ik-1 end do jk = jk+1 end do jper = 1 endif ! if one of the non-zero elements of the new row corresponds to one of ! the b-splines n(j;*),j=n7+1,...,n4,we take account of the periodicity ! conditions for setting up this row of (a). h1 = zero h2 = zero j = nrold-n11 do i=1,5 j = j+1 l0 = j l1 = l0-4 do while (l1>max(0,n11)) l0 = l1-n11 l1 = l0-4 end do if (l1>0) then h1(l1) = h(i) else h2(l0) = h2(l0) + h(i) endif end do ! rotate the new row of (a) into triangle. if (n11>0) then ! rotations with the rows 1,2,...,n11 of (a). one_to_n11: do irot=1,n11 piv = h1(1) i2 = min(n11-irot,4) if (not_equal(piv,zero)) then ! calculate the parameters of the givens transformation. call fpgivs(piv,a(irot,1),co,si) ! apply that transformation to the columns of matrix q. j = 0 do ii=1,idim l = (ii-1)*m3+irot do jj=1,mm j = j+1 call fprota(co,si,right(j),q(l)) l = l+n7 end do end do ! apply that transformation to the rows of (a) with respect to aa. call fprota(co,si,h2,aa(irot,:)) ! apply that transformation to the rows of (a) with respect to a. if (i2==0) exit one_to_n11 call fprota(co,si,h1(2:i2+1),a(irot,2:i2+1)) endif h1(1:i2+1) = [h1(2:i2+1),zero] end do one_to_n11 endif ! rotations with the rows n11+1,...,n7 of a. n11_to_n7: do irot=1,4 ij = n11+irot; if (ij<=0) cycle n11_to_n7 piv = h2(irot); if (equal(piv,zero)) cycle n11_to_n7 ! calculate the parameters of the givens transformation. call fpgivs(piv,aa(ij,irot),co,si) ! apply that transformation to the columns of matrix q. j = 0 do ii=1,idim l = (ii-1)*m3+ij do jj=1,mm j = j+1 call fprota(co,si,right(j),q(l)) l = l+n7 end do end do ! apply that transformation to the rows of (a) with respect to aa. if (irot<4) call fprota(co,si,h2(irot+1:),aa(ij,irot+1:)) end do n11_to_n7 else ! rotation into triangle of the new row of (a), in case the elements ! corresponding to the b-splines n(j;*),j=n7+1,...,n4 are all zero. irot =nrold all_zero: do i=1,5 irot = irot+1 piv = h(i) if (equal(piv,zero)) cycle all_zero ! calculate the parameters of the givens transformation. call fpgivs(piv,a(irot,1),co,si) ! apply that transformation to the columns of matrix g. j = 0 do ii=1,idim l = (ii-1)*m3+irot do jj=1,mm j = j+1 call fprota(co,si,right(j),q(l)) l = l+n7 end do end do ! apply that transformation to the rows of (a). if (i<5) call fprota(co,si,h(i+1:5),a(irot,2:6-i)) end do all_zero endif if (nrold==number) exit inner nrold = nrold+1 end do inner end do iterate return end subroutine fptrpe ! subroutine insert inserts a new knot x into a spline function s(x) of degree k and calculates ! the b-spline representation of s(x) with respect to the new set of knots. in addition, if ! iopt/=0, s(x) will be considered as a periodic spline with period per=t(n-k)-t(k+1) satisfying ! the boundary constraints ! t(i+n-2*k-1) = t(i)+per ,i=1,2,...,2*k+1 ! c(i+n-2*k-1) = c(i) ,i=1,2,...,k ! in that case, the knots and b-spline coefficients returned will also satisfy the periodic BCs, i.e. ! tt(i+nn-2*k-1) = tt(i)+per ,i=1,2,...,2*k+1 ! cc(i+nn-2*k-1) = cc(i) ,i=1,2,...,k pure subroutine insert(iopt,t,n,c,k,x,tt,nn,cc,nest,ier) ! ! calling sequence: ! call insert(iopt,t,n,c,k,x,tt,nn,cc,nest,ier) ! ! input parameters: ! iopt : integer flag, specifying whether (iopt/=0) or not (iopt=0) the given spline must be ! considered as being periodic. ! t : array,length nest, which contains the position of the knots. ! n : integer, giving the total number of knots of s(x). ! c : array,length nest, which contains the b-spline coefficients. ! k : integer, giving the degree of s(x). ! x : real, which gives the location of the knot to be inserted. ! nest : integer specifying the dimension of the arrays t,c,tt and cc. nest > n. ! ! output parameters: ! tt : array,length nest, which contains the position of the knots after insertion. ! nn : integer, giving the total number of knots after insertion ! cc : array,length nest, which contains the b-spline coefficients of s(x) with respect to the ! new set of knots. ! ier : error flag ! ier = 0 : normal return ! ier =10 : invalid input data (see restrictions) ! ! restrictions: ! nest > n ! t(k+1) <= x <= t(n-k) ! in case of a periodic spline (iopt/=0) there must be either at least k interior knots t(j) ! satisfying t(k+1)<t(j)<=x or at least k interior knots t(j) satisfying x<=t(j)<t(n-k) ! ! other subroutines required: fpinst. ! ! further comments: ! subroutine insert may be called as follows ! call insert(iopt,t,n,c,k,x,t,n,c,nest,ier) ! in which case the new representation will simply replace the old one ! ! references : ! boehm w : inserting new knots into b-spline curves. computer aided design 12 (1980) 199-201. ! dierckx p. : curve and surface fitting with splines, monographs on numerical analysis, oxford ! university press, 1993. ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! ..scalar arguments.. integer, intent(in) :: iopt,n,k,nest integer, intent(out) :: nn,ier real(RKIND), intent(in) :: x ! ..array arguments.. real(RKIND), intent(in) :: t(nest),c(nest) real(RKIND), intent(out) :: tt(nest),cc(nest) ! ..local scalars.. integer :: kk,k1,l,nk,nk1 ! .. ! before starting computations a data check is made. if the input data ! are invalid control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR if (nest<=n) return k1 = k+1 nk = n-k if (x<t(k1) .or. x>t(nk)) return ! search for knot interval t(l) <= x < t(l+1). nk1 = nk-1 l = k1 do while (x>=t(l+1) .and. l/=nk1) l = l+1 end do ! no interval found in whole range if(t(l)>=t(l+1)) return if(iopt/=0) then kk = 2*k if (l<=kk .and. l>=(n-kk)) return endif ier = FITPACK_OK ! insert the new knot. call fpinst(iopt,t,n,c,k,x,l,tt,nn,cc,nest) end subroutine insert ! Subroutine insert_inplace was created to avoid alising issues with the original package ! that called subroutine insert with the same variables as input and output arguments pure subroutine insert_inplace(iopt,t,n,c,k,x,nest,ier) integer, intent(in) :: iopt,k,nest integer, intent(out) :: ier real(RKIND), intent(in) :: x integer, intent(inout) :: n ! ..array arguments.. real(RKIND), intent(inout) :: t(nest),c(nest) ! Temporary storage real(RKIND) :: tt(nest),cc(nest) integer :: nn ! Call insert call insert(iopt,t,n,c,k,x,tt,nn,cc,nest,ier) ! Return values t = tt c = cc n = nn end subroutine insert_inplace pure subroutine parcur(iopt,ipar,idim,m,u,mx,x,w,ub,ue,k,s,nest,n,t,nc,c,fp,wrk,lwrk,iwrk,ier) ! given the ordered set of m points x(i) in the idim-dimensional space and given also a corresponding ! set of strictly increasing values u(i) and the set of positive numbers w(i),i=1,2,...,m, subroutine ! parcur determines a smooth approximating spline curve s(u), i.e. ! x1 = s1(u) ! x2 = s2(u) ub <= u <= ue ! ......... ! xidim = sidim(u) ! with sj(u),j=1,2,...,idim spline functions of degree k with common knots t(j),j=1,2,...,n. ! if ipar=1 the values ub,ue and u(i),i=1,2,...,m must be supplied by the user. if ipar=0 these values ! are chosen automatically by parcur as ! v(1) = 0 ! v(i) = v(i-1) + dist(x(i),x(i-1)) ,i=2,3,...,m ! u(i) = v(i)/v(m) ,i=1,2,...,m ! ub = u(1) = 0, ue = u(m) = 1. ! if iopt=-1 parcur calculates the weighted least-squares spline according to a given set of knots. ! if iopt>=0 the number of knots of the splines sj(u) and the position t(j),j=1,2,...,n is chosen ! automatically by the routine. the smoothness of s(u) is then achieved by minimalizing the ! discontinuity jumps of the k-th derivative of s(u) at the knots t(j),j=k+2,k+3,...,n-k-1. the amount ! of smoothness is determined by the condition that f(p)=sum((w(i)*dist(x(i),s(u(i))))**2) be <= s, ! with s a given non-negative constant, called the smoothing factor. the fit s(u) is given in the ! b-spline representation and can be evaluated by means of subroutine curev. ! ! calling sequence: ! call parcur(iopt,ipar,idim,m,u,mx,x,w,ub,ue,k,s,nest,n,t,nc,c,fp,wrk,lwrk,iwrk,ier) ! ! parameters: ! iopt : integer flag. on entry iopt must specify whether a weighted least-squares spline curve ! (iopt=-1) or a smoothing spline curve (iopt=0 or 1) must be determined.if iopt=0 the routine ! will start with an initial set of knots t(i)=ub,t(i+k+1)=ue, i=1,2,...,k+1. if iopt=1 ! the routine will continue with the knots found at the last call of the routine. attention: ! a call with iopt=1 must always be immediately preceded by another call with iopt=1 or iopt=0. ! unchanged on exit. ! ipar : integer flag. on entry ipar must specify whether (ipar=1) the user will supply the parameter ! values u(i),ub and ue or whether (ipar=0) these values are to be calculated by parcur. ! unchanged on exit. ! idim : integer. on entry idim must specify the dimension of the curve. 0 < idim < 11. ! unchanged on exit. ! m : integer. on entry m must specify the number of data points. m > k. unchanged on exit. ! u : real array of dimension at least (m). in case ipar=1,before entry, u(i) must be set to the ! i-th value of the parameter variable u for i=1,2,...,m. these values must then be supplied ! in strictly ascending order and will be unchanged on exit. in case ipar=0, on exit,array ! u will contain the values u(i) as determined by parcur. ! mx : integer. on entry mx must specify the actual dimension of the array x as declared in the ! calling (sub)program. mx must not be too small (see x). unchanged on exit. ! x : real array of dimension at least idim*m. ! before entry, x(idim*(i-1)+j) must contain the j-th coordinate of the i-th data point for ! i=1,2,...,m and j=1,2,...,idim. unchanged on exit. ! w : real array of dimension at least (m). before entry, w(i) must be set to the i-th value in ! the set of weights. the w(i) must be strictly positive. unchanged on exit. see also further ! comments. ! ub,ue : real values. on entry (in case ipar=1) ub and ue must contain the lower and upper bound for ! the parameter u. ub <=u(1), ue>= u(m). if ipar = 0 these values will automatically be set ! to 0 and 1 by parcur. ! k : integer. on entry k must specify the degree of the splines. 1<=k<=5. it is recommended to ! use cubic splines (k=3). the user is strongly dissuaded from choosing k even,together ! with a small s-value. unchanged on exit. ! s : real.on entry (in case iopt>=0) s must specify the smoothing factor. s >=0. unchanged on exit. ! for advice on the choice of s see further comments. ! nest : integer. on entry nest must contain an over-estimate of the total number of knots of the ! splines returned, to indicate the storage space available to the routine. nest >=2*k+2. ! in most practical situation nest=m/2 will be sufficient. always large enough is nest=m+k+1, ! the number of knots needed for interpolation (s=0). unchanged on exit. ! n : integer. ! unless ier = 10 (in case iopt >=0), n will contain the total number of knots of the ! smoothing spline curve returned if the computation mode iopt=1 is used this value of n ! should be left unchanged between subsequent calls. in case iopt=-1, the value of n must be ! specified on entry. ! t : real array of dimension at least (nest). ! on successful exit, this array will contain the knots of the spline curve,i.e. the position ! of the interior knots t(k+2),t(k+3),..,t(n-k-1) as well as the position of the additional ! t(1)=t(2)=...=t(k+1)=ub and t(n-k)=...=t(n)=ue needed for the b-spline representation. ! if the computation mode iopt=1 is used, the values of t(1),t(2),...,t(n) should be left ! unchanged between subsequent calls. if the computation mode iopt=-1 is used, the values ! t(k+2),...,t(n-k-1) must be supplied by the user, before entry. see also the restrictions ! (ier=10). ! nc : integer. on entry nc must specify the actual dimension of the array c as declared in the ! calling (sub)program. nc must not be too small (see c). unchanged on exit. ! c : real array of dimension at least (nest*idim). on successful exit, this array will contain ! the coefficients in the b-spline representation of the spline curve s(u),i.e. the b-spline ! coefficients of the spline sj(u) will be given in c(n*(j-1)+i),i=1,2,...,n-k-1 for ! j=1,2,...,idim. ! fp : real. unless ier = 10, fp contains the weighted sum of squared residuals of the spline ! curve returned. ! wrk : real array of dimension at least m*(k+1)+nest*(6+idim+3*k). used as working space. if the ! computation mode iopt=1 is used, the values wrk(1),...,wrk(n) should be left unchanged ! between subsequent calls. ! lwrk : integer. on entry,lwrk must specify the actual dimension of the array wrk as declared in ! the calling (sub)program. lwrk must not be too small (see wrk). unchanged on exit. ! iwrk : integer array of dimension at least (nest). used as working space. if the computation ! mode iopt=1 is used,the values iwrk(1),...,iwrk(n) should be left unchanged between ! subsequent calls. ! ier : integer. unless the routine detects an error, ier contains a non-positive value on exit, i.e. ! ier=0 : normal return. the curve returned has a residual sum of squares fp such that abs(fp-s)/s ! <= tol with tol a relative tolerance set to 0.001 by the program. ! ier=-1 : normal return. the curve returned is an interpolating spline curve (fp=0). ! ier=-2 : normal return. the curve returned is the weighted least squares polynomial curve of ! degree k. in this extreme case fp gives the upper bound fp0 for the smoothing factor s. ! ier=1 : error. the required storage space exceeds the available storage space, as specified by the ! parameter nest. ! likely causes : nest too small. if nest is already large (say nest > m/2), it may also ! indicate that s is too small. the approximation returned is the least-squares spline ! curve according to the knots t(1),t(2),...,t(n). (n=nest) the parameter fp gives the ! corresponding weighted sum of squared residuals (fp>s). ! ier=2 : error. a theoretically impossible result was found during the iteration process for ! finding a smoothing spline curve with fp = s. probably causes : s too small. ! there is an approximation returned but the corresponding weighted sum of squared residuals ! does not satisfy the condition abs(fp-s)/s < tol. ! ier=3 : error. the maximal number of iterations maxit (set to 20 by the program) allowed for ! finding a smoothing curve with fp=s has been reached. probably causes : s too small ! there is an approximation returned but the corresponding weighted sum of squared residuals ! does not satisfy the condition abs(fp-s)/s < tol. ! ier=10 : error. on entry, the input data are controlled on validity the following restrictions ! must be satisfied. ! -1<=iopt<=1, 1<=k<=5, m>k, nest>2*k+2, w(i)>0,i=1,2,...,m ! 0<=ipar<=1, 0<idim<=10, lwrk>=(k+1)*m+nest*(6+idim+3*k), ! nc>=nest*idim ! if ipar=0: sum j=1,idim (x(idim*i+j)-x(idim*(i-1)+j))**2>0 i=1,2,...,m-1. ! if ipar=1: ub<=u(1)<u(2)<...<u(m)<=ue ! if iopt=-1: 2*k+2<=n<=min(nest,m+k+1) ! ub<t(k+2)<t(k+3)<...<t(n-k-1)<ue ! (ub=0 and ue=1 in case ipar=0) ! the schoenberg-whitney conditions, i.e. there must be a subset of data points ! uu(j) such that ! t(j) < uu(j) < t(j+k+1), j=1,2,...,n-k-1 ! if iopt>=0: s>=0 ! if s=0 : nest >= m+k+1 ! if one of these conditions is found to be violated,control is immediately repassed to the ! calling program. in that case there is no approximation returned. ! ! further comments: ! by means of the parameter s, the user can control the tradeoff between closeness of fit and ! smoothness of fit of the approximation. if s is too large, the curve will be too smooth and signal ! will be lost ; if s is too small the curve will pick up too much noise. in the extreme cases the ! program will return an interpolating curve if s=0 and the least-squares polynomial curve of degree ! k if s is very large. between these extremes, a properly chosen s will result in a good compromise ! between closeness of fit and smoothness of fit. to decide whether an approximation, corresponding ! to a certain s is satisfactory the user is highly recommended to inspect the fits graphically. ! recommended values for s depend on the weights w(i). if these are taken as 1/d(i) with d(i) an ! estimate of the standard deviation of x(i), a good s-value should be found in the range ! (m-sqrt(2*m),m+sqrt(2*m)). if nothing is known about the statistical error in x(i) each w(i) can be ! set equal to one and s determined by trial and error, taking account of the comments above. the best ! is then to start with a very large value of s ( to determine the least-squares polynomial curve and ! the upper bound fp0 for s) and then to progressively decrease the value of s ( say by a factor 10 ! in the beginning, i.e. s=fp0/10, fp0/100,...and more carefully as the approximating curve shows ! more detail) to obtain closer fits. to economize the search for a good s-value the program provides ! with different modes of computation. at the first call of the routine, or whenever he wants to ! restart with the initial set of knots the user must set iopt=0. if iopt=1 the program will continue ! with the set of knots found at the last call of the routine. this will save a lot of computation ! time if parcur is called repeatedly for different values of s. the number of knots of the spline ! returned and their location will depend on the value of s and on the complexity of the shape of the ! curve underlying the data. but, if the computation mode iopt=1 is used, the knots returned may also ! depend on the s-values at previous calls (if these were smaller). therefore, if after a number of ! trials with different s-values and iopt=1, the user can finally accept a fit as satisfactory, it ! may be worthwhile for him to call parcur once more with the selected value for s but now with iopt=0. ! indeed, parcur may then return an approximation of the same quality of fit but with fewer knots and ! therefore better if data reduction is also an important objective for the user. ! ! the form of the approximating curve can strongly be affected by the choice of the parameter values ! u(i). if there is no physical reason for choosing a particular parameter u, often good results will ! be obtained with the choice of parcur (in case ipar=0), i.e. ! v(1)=0, v(i)=v(i-1)+q(i), i=2,...,m, u(i)=v(i)/v(m), i=1,..,m ! where ! q(i)= sqrt(sum j=1,idim (xj(i)-xj(i-1))**2 ) ! other possibilities for q(i) are ! q(i)= sum j=1,idim (xj(i)-xj(i-1))**2 ! q(i)= sum j=1,idim abs(xj(i)-xj(i-1)) ! q(i)= max j=1,idim abs(xj(i)-xj(i-1)) ! q(i)= 1 ! ! other subroutines required: ! fpback,fpbspl,fpchec,fppara,fpdisc,fpgivs,fpknot,fprati,fprota ! ! references: ! dierckx p. : algorithms for smoothing data with periodic and parametric splines, computer graphics ! and image processing 20 (1982) 171-184. ! dierckx p. : algorithms for smoothing data with periodic and parametric splines, report tw55, dept. ! computer science, k.u.leuven, 1981. ! dierckx p. : curve and surface fitting with splines, monographs on numerical analysis, oxford ! university press, 1993. ! ! author: ! p.dierckx ! dept. computer science, k.u. leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! creation date : may 1979 ! ! ..scalar arguments.. real(RKIND), intent(inout) :: ub,ue,s real(RKIND), intent(out) :: fp integer, intent(in) :: iopt,ipar,idim,m,mx,k,nest,lwrk,nc integer, intent(inout) :: n integer, intent(out) :: ier ! ..array arguments.. real(RKIND), intent(in) :: x(idim,m) real(RKIND), intent(inout) :: u(m),w(m),t(nest),c(nc),wrk(lwrk) integer, intent(inout) :: iwrk(nest) ! ..local scalars.. integer i,ia,ib,ifp,ig,iq,iz,j,k1,k2,lwest,nmin,ncc ! we set up the parameters tol and maxit integer, parameter :: maxit = 20 real(RKIND), parameter :: tol = smallnum03 ! before starting computations a data check is made. if the input data ! are invalid, control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR if (iopt<(-1) .or. iopt>1) return if (ipar<0 .or. ipar>1) return if (idim<=0 .or. idim>MAX_IDIM) return if (k<=0 .or. k>5) return k1 = k+1 k2 = k1+1 nmin = 2*k1 if (m<k1 .or. nest<nmin) return ncc = nest*idim if (mx<m*idim .or. nc<ncc) return lwest = m*k1+nest*(6+idim+3*k) if (lwrk<lwest) return ! Normalize coordinates if (ipar==0 .and. iopt<=0) then ! Point coordinates are stored in x(:), offset by idim values u(1) = zero do i=2,m u(i) = u(i-1) + norm2(x(:,i)-x(:,i-1)) end do if (u(m)<=zero) return u(2:) = u(2:)/u(m) ub = zero ue = one u(m) = ue endif if (ub>u(1) .or. ue<u(m) .or. w(1)<=zero) return if (any(u(1:m-1)>=u(2:) .or. w(2:)<=zero)) return if (iopt<0) then if (n<nmin .or. n>nest) return j = n do i=1,k1 t(i) = ub t(j) = ue j = j-1 end do ier = fpchec(u,m,t,n,k); if (ier/=FITPACK_OK) return else if (s<zero) return if (equal(s,zero) .and. nest<(m+k1)) return ier = FITPACK_OK endif ! we partition the working space and determine the spline curve. ifp = 1 iz = ifp+nest ia = iz+ncc ib = ia+nest*k1 ig = ib+nest*k2 iq = ig+nest*k2 call fppara(iopt,idim,m,u,mx,x,w,ub,ue,k,s,nest,tol,maxit,k1,k2, & n,t,ncc,c,fp,wrk(ifp),wrk(iz),wrk(ia),wrk(ib),wrk(ig),wrk(iq),iwrk,ier) return end subroutine parcur pure subroutine parder(tx,nx,ty,ny,c,kx,ky,nux,nuy,x,mx,y,my,z,wrk,lwrk,iwrk,kwrk,ier) ! subroutine parder evaluates on a grid (x(i),y(j)),i=1,...,mx; j=1,...,my the partial derivative ! (order nux,nuy) of a bivariate spline s(x,y) of degrees kx and ky, given in the b-spline ! representation. ! ! calling sequence: ! call parder(tx,nx,ty,ny,c,kx,ky,nux,nuy,x,mx,y,my,z,wrk,lwrk,iwrk,kwrk,ier) ! ! input parameters: ! tx : real array, length nx, which contains the position of the knots in the x-direction. ! nx : integer, giving the total number of knots in the x-direction ! ty : real array, length ny, which contains the position of the knots in the y-direction. ! ny : integer, giving the total number of knots in the y-direction ! c : real array, length (nx-kx-1)*(ny-ky-1), which contains the b-spline coefficients. ! kx,ky : integer values, giving the degrees of the spline. ! nux/y : integer values, specifying the order of the partial derivative. 0<=nux<kx, 0<=nuy<ky. ! x : real array of dimension (mx). ! before entry x(i) must be set to the x co-ordinate of the i-th grid point along the x-axis. ! tx(kx+1)<=x(i-1)<=x(i)<=tx(nx-kx), i=2,...,mx. ! mx : on entry mx must specify the number of grid points along the x-axis. mx >=1. ! y : real array of dimension (my). ! before entry y(j) must be set to the y co-ordinate of the j-th grid point along the y-axis. ! ty(ky+1)<=y(j-1)<=y(j)<=ty(ny-ky), j=2,...,my. ! my : on entry my must specify the number of grid points along the y-axis. my >=1. ! wrk : real array of dimension lwrk. used as workspace. ! lwrk : integer, specifying the dimension of wrk. ! lwrk >= mx*(kx+1-nux)+my*(ky+1-nuy)+(nx-kx-1)*(ny-ky-1) ! iwrk : integer array of dimension kwrk. used as workspace. ! kwrk : integer, specifying the dimension of iwrk. kwrk >= mx+my. ! ! output parameters: ! z : real array of dimension (mx*my). ! on successful exit z(my*(i-1)+j) contains the value of the specified partial derivative of ! s(x,y) at the point (x(i),y(j)),i=1,...,mx;j=1,...,my. ! ier : integer error flag ! ier=0 : normal return ! ier=10: invalid input data (see restrictions) ! ! restrictions: ! mx >=1, my >=1, 0 <= nux < kx, 0 <= nuy < ky, kwrk>=mx+my ! lwrk>=mx*(kx+1-nux)+my*(ky+1-nuy)+(nx-kx-1)*(ny-ky-1), ! tx(kx+1) <= x(i-1) <= x(i) <= tx(nx-kx), i=2,...,mx ! ty(ky+1) <= y(j-1) <= y(j) <= ty(ny-ky), j=2,...,my ! ! other subroutines required: ! fpbisp,fpbspl ! ! references : ! de boor c : on calculating with b-splines, j. approximation theory 6 (1972) 50-62. ! dierckx p. : curve and surface fitting with splines, monographs on numerical analysis, oxford ! university press, 1993. ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! ..scalar arguments.. integer, intent(in) :: nx,ny,kx,ky,nux,nuy,mx,my,lwrk,kwrk integer, intent(out) :: ier ! ..array arguments.. integer, intent(inout) :: iwrk(kwrk) real(RKIND), intent(in) :: tx(nx),ty(ny),c((nx-kx-1)*(ny-ky-1)),x(mx),y(my) real(RKIND), intent(out) :: z(mx*my) real(RKIND), intent(inout) :: wrk(lwrk) ! ..local scalars.. integer :: i,iwx,iwy,j,kkx,kky,kx1,ky1,lx,ly,lwest,l1,l2,m,m0,m1,nc,nkx1,nky1,nxx,nyy real(RKIND) :: ak,fac ! .. ! before starting computations a data check is made. if the input data ! are invalid control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR kx1 = kx+1 ky1 = ky+1 nkx1 = nx-kx1 nky1 = ny-ky1 nc = nkx1*nky1 lwest = nc +(kx1-nux)*mx+(ky1-nuy)*my if (nux<0 .or. nux>=kx) return if (nuy<0 .or. nuy>=ky) return if (lwrk<lwest) return if (kwrk<(mx+my)) return if (mx<1 .or. my<1) return if (mx>1 .and. any(x(2:mx)<x(1:mx-1))) return if (my>1 .and. any(y(2:my)<y(1:my-1))) return ! All checks passed ier = FITPACK_OK nxx = nkx1 nyy = nky1 kkx = kx kky = ky ! the partial derivative of order (nux,nuy) of a bivariate spline of degrees kx,ky is a bivariate ! spline of degrees kx-nux,ky-nuy. we calculate the b-spline coefficients of this spline wrk(1:nc) = c(1:nc) if (nux>0) then lx = 1 x_deriv_order: do j=1,nux ak = kkx nxx = nxx-1 l1 = lx m0 = 1 do i=1,nxx l1 = l1+1 l2 = l1+kkx fac = tx(l2)-tx(l1) if (fac>zero) then do m=1,nyy m1 = m0+nyy wrk(m0) = (wrk(m1)-wrk(m0))*ak/fac m0 = m0+1 end do endif end do lx = lx+1 kkx = kkx-1 end do x_deriv_order endif if (nuy>0) then ly = 1 y_deriv_order: do j=1,nuy ak = kky nyy = nyy-1 l1 = ly do i=1,nyy l1 = l1+1 l2 = l1+kky fac = ty(l2)-ty(l1) if (fac>zero) then m0 = i do m=1,nxx m1 = m0+1 wrk(m0) = (wrk(m1)-wrk(m0))*ak/fac m0 = m0+nky1 end do endif end do ly = ly+1 kky = kky-1 end do y_deriv_order m0 = nyy m1 = nky1 do m=2,nxx do i=1,nyy m0 = m0+1 m1 = m1+1 wrk(m0) = wrk(m1) end do m1 = m1+nuy end do endif ! we partition the working space and evaluate the partial derivative iwx = 1+nxx*nyy iwy = iwx+mx*(kx1-nux) call fpbisp(tx(nux+1),nx-2*nux,ty(nuy+1),ny-2*nuy,wrk,kkx,kky, & x,mx,y,my,z,wrk(iwx),wrk(iwy),iwrk(1),iwrk(mx+1)) return end subroutine parder pure subroutine pardeu(tx,nx,ty,ny,c,kx,ky,nux,nuy,x,y,z,m,wrk,lwrk,iwrk,kwrk,ier) ! subroutine pardeu evaluates on a set of points (x(i),y(i)),i=1,...,m the partial derivative ! ( order nux,nuy) of a bivariate spline s(x,y) of degrees kx and ky, given in the b-spline ! representation. ! ! calling sequence: ! call pardeu(tx,nx,ty,ny,c,kx,ky,nux,nuy,x,y,z,m,wrk,lwrk,iwrk,kwrk,ier) ! ! input parameters: ! tx : real array, length nx, which contains the position of the knots in the x-direction. ! nx : integer, giving the total number of knots in the x-direction ! ty : real array, length ny, which contains the position of the knots in the y-direction. ! ny : integer, giving the total number of knots in the y-direction ! c : real array, length (nx-kx-1)*(ny-ky-1), which contains the b-spline coefficients. ! kx,ky : integer values, giving the degrees of the spline. ! nux/y : integer values, specifying the order of the partial derivative. 0<=nux<kx, 0<=nuy<ky. ! kx,ky : integer values, giving the degrees of the spline. ! x : real array of dimension (mx). ! y : real array of dimension (my). ! m : on entry m must specify the number points. m >= 1. ! wrk : real array of dimension lwrk. used as workspace. ! lwrk : integer, specifying the dimension of wrk. ! lwrk >= mx*(kx+1-nux)+my*(ky+1-nuy)+(nx-kx-1)*(ny-ky-1) ! iwrk : integer array of dimension kwrk. used as workspace. ! kwrk : integer, specifying the dimension of iwrk. kwrk >= mx+my. ! ! output parameters: ! z : real array of dimension (m). ! on successful exit z(i) contains the value of the specified partial derivative of s(x,y) ! at the point (x(i),y(i)),i=1,...,m. ! ier : integer error flag ! ! restrictions: ! lwrk>=m*(kx+1-nux)+m*(ky+1-nuy)+(nx-kx-1)*(ny-ky-1), ! ! other subroutines required: ! fpbisp,fpbspl ! ! references : ! de boor c : on calculating with b-splines, j. approximation theory 6 (1972) 50-62. ! dierckx p. : curve and surface fitting with splines, oxford university press, 1993. ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! ! ..scalar arguments.. integer , intent(in) :: nx,ny,kx,ky,m,lwrk,kwrk,nux,nuy integer , intent(out) :: ier ! ..array arguments.. integer , intent(inout) :: iwrk(kwrk) real(RKIND), intent(in) :: tx(nx),ty(ny),c((nx-kx-1)*(ny-ky-1)),x(m),y(m) real(RKIND), intent(out) :: z(m) real(RKIND), intent(inout) :: wrk(lwrk) ! ..local scalars.. integer :: i,iwx,iwy,j,kkx,kky,kx1,ky1,lx,ly,lwest,l1,l2,mm,m0,m1,nc,nkx1,nky1,nxx,nyy real(RKIND) :: ak,fac ! .. ! before starting computations a data check is made. if the input data are invalid control is ! immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR kx1 = kx+1 ky1 = ky+1 nkx1 = nx-kx1 nky1 = ny-ky1 nc = nkx1*nky1 lwest = nc +(kx1-nux)*m+(ky1-nuy)*m if (nux<0 .or. nux>=kx) return if (nuy<0 .or. nuy>=ky) return if (lwrk<lwest) return if (kwrk<(m+m)) return if (m<1) return ier = FITPACK_OK nxx = nkx1 nyy = nky1 kkx = kx kky = ky ! the partial derivative of order (nux,nuy) of a bivariate spline of degrees kx,ky is a bivariate ! spline of degrees kx-nux,ky-nuy. we calculate the b-spline coefficients of this spline wrk(:nc) = c(:nc) if (nux>0) then lx = 1 x_deriv_order: do j=1,nux ak = kkx nxx = nxx-1 l1 = lx m0 = 1 do i=1,nxx l1 = l1+1 l2 = l1+kkx fac = tx(l2)-tx(l1) if (fac>zero) THEN do mm=1,nyy m1 = m0+nyy wrk(m0) = (wrk(m1)-wrk(m0))*ak/fac m0 = m0+1 end do endif end do lx = lx+1 kkx = kkx-1 end do x_deriv_order endif if (nuy>0) then ly = 1 y_deriv_order: do j=1,nuy ak = kky nyy = nyy-1 l1 = ly do i=1,nyy l1 = l1+1 l2 = l1+kky fac = ty(l2)-ty(l1) if (fac>zero) then m0 = i do mm=1,nxx m1 = m0+1 wrk(m0) = (wrk(m1)-wrk(m0))*ak/fac m0 = m0+nky1 end do endif end do ly = ly+1 kky = kky-1 end do y_deriv_order m0 = nyy m1 = nky1 do mm=2,nxx do i=1,nyy m0 = m0+1 m1 = m1+1 wrk(m0) = wrk(m1) end do m1 = m1+nuy end do endif ! we partition the working space and evaluate the partial derivative iwx = 1+nxx*nyy iwy = iwx+m*(kx1-nux) do i=1,m call fpbisp(tx(nux+1),nx-2*nux,ty(nuy+1),ny-2*nuy,wrk,kkx,kky, & x(i),1,y(i),1,z(i),wrk(iwx),wrk(iwy),iwrk(1),iwrk(2)) end do return end subroutine pardeu pure subroutine pardtc(tx,nx,ty,ny,c,kx,ky,nux,nuy,newc,ier) ! subroutine pardtc takes the knots and coefficients of a bivariate spline, and returns the ! coefficients for a new bivariate spline that evaluates the partial derivative (order nux, nuy) of ! the original spline. ! ! calling sequence: ! call pardtc(tx,nx,ty,ny,c,kx,ky,nux,nuy,newc,ier) ! ! input parameters: ! tx : real array, length nx, which contains the position of the knots in the x-direction. ! nx : integer, giving the total number of knots in the x-direction (hidden) ! ty : real array, length ny, which contains the position of the knots in the y-direction. ! ny : integer, giving the total number of knots in the y-direction (hidden) ! c : real array, length (nx-kx-1)*(ny-ky-1), which contains the b-spline coefficients. ! kx,ky : integer values, giving the degrees of the spline. ! nux : integer values, specifying the order of the partial ! nuy derivative. 0<=nux<kx, 0<=nuy<ky. ! ! output parameters: ! newc : real array containing the coefficients of the derivative. ! the dimension is (nx-nux-kx-1)*(ny-nuy-ky-1). ! ier : integer error flag ! ! restrictions: ! 0 <= nux < kx, 0 <= nuy < kyc ! ! other subroutines required: ! none ! ! references : ! de boor c : on calculating with b-splines, j. approximation theory 6 (1972) 50-62. ! dierckx p. : curve and surface fitting with splines, oxford university press, 1993. ! ! based on the subroutine "parder" by Paul Dierckx. ! ! author : ! Cong Ma ! Department of Mathematics and Applied Mathematics, U. of Cape Town ! Cross Campus Road, Rondebosch 7700, Cape Town, South Africa. ! e-mail : cong.ma@uct.ac.za ! ! ..scalar arguments.. integer, intent(in) :: nx,ny,kx,ky,nux,nuy integer, intent(out) :: ier ! ..array arguments.. real(RKIND), intent(in) :: tx(nx),ty(ny),c((nx-kx-1)*(ny-ky-1)) real(RKIND), intent(out) :: newc((nx-kx-1)*(ny-ky-1)) ! ..local scalars.. integer :: i,j,kx1,ky1,lx,ly,l1,l2,m,m0,m1,nkx1,nky1,nxx,nyy,newkx,newky,nc real(RKIND) ak,fac ! .. ! before starting computations a data check is made. if the input data ! are invalid control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR if (nux<0 .or. nux>=kx) return if (nuy<0 .or. nuy>=ky) return kx1 = kx+1 ky1 = ky+1 nkx1 = nx-kx1 nky1 = ny-ky1 nc = nkx1*nky1 ier = FITPACK_OK nxx = nkx1 nyy = nky1 newkx = kx newky = ky ! the partial derivative of order (nux,nuy) of a bivariate spline of degrees kx,ky is a bivariate ! spline of degrees kx-nux,ky-nuy. we calculate the b-spline coefficients of this spline ! that is to say newkx = kx - nux, newky = ky - nuy newc(:nc) = c(:nc) if (nux>0) then lx = 1 x_deriv_order: do j=1,nux ak = newkx nxx = nxx-1 l1 = lx m0 = 1 do i=1,nxx l1 = l1+1 l2 = l1+newkx fac = tx(l2)-tx(l1) if (fac>zero) then do m=1,nyy m1 = m0+nyy newc(m0) = (newc(m1)-newc(m0))*ak/fac m0 = m0+1 end do endif end do lx = lx+1 newkx = newkx-1 end do x_deriv_order endif if (nuy>0) then ly = 1 y_deriv_order: do j=1,nuy ak = newky nyy = nyy-1 l1 = ly do i=1,nyy l1 = l1+1 l2 = l1+newky fac = ty(l2)-ty(l1) if (fac>zero) then m0 = i do m=1,nxx m1 = m0+1 newc(m0) = (newc(m1)-newc(m0))*ak/fac m0 = m0+nky1 end do endif end do ly = ly+1 newky = newky-1 end do y_deriv_order m0 = nyy m1 = nky1 do m=2,nxx do i=1,nyy m0 = m0+1 m1 = m1+1 newc(m0) = newc(m1) end do m1 = m1+nuy end do endif return end subroutine pardtc pure subroutine parsur(iopt,ipar,idim,mu,u,mv,v,f,s,nuest, & nvest,nu,tu,nv,tv,c,fp,wrk,lwrk,iwrk,kwrk,ier) ! given the set of ordered points f(i,j) in the idim-dimensional space, ! corresponding to grid values (u(i),v(j)) ,i=1,...,mu ; j=1,...,mv, ! parsur determines a smooth approximating spline surface s(u,v) , i.e. ! f1 = s1(u,v) ! ... u(1) <= u <= u(mu) ; v(1) <= v <= v(mv) ! fidim = sidim(u,v) ! with sl(u,v), l=1,2,...,idim bicubic spline functions with common ! knots tu(i),i=1,...,nu in the u-variable and tv(j),j=1,...,nv in the ! v-variable. ! in addition, these splines will be periodic in the variable u if ! ipar(1) = 1 and periodic in the variable v if ipar(2) = 1. ! if iopt=-1, parsur determines the least-squares bicubic spline ! surface according to a given set of knots. ! if iopt>=0, the number of knots of s(u,v) and their position ! is chosen automatically by the routine. the smoothness of s(u,v) is ! achieved by minimalizing the discontinuity jumps of the derivatives ! of the splines at the knots. the amount of smoothness of s(u,v) is ! determined by the condition that ! fp=sumi=1,mu(sumj=1,mv(dist(f(i,j)-s(u(i),v(j)))**2))<=s, ! with s a given non-negative constant. ! the fit s(u,v) is given in its b-spline representation and can be ! evaluated by means of routine surev. ! ! calling sequence: ! call parsur(iopt,ipar,idim,mu,u,mv,v,f,s,nuest,nvest,nu,tu, ! * nv,tv,c,fp,wrk,lwrk,iwrk,kwrk,ier) ! ! parameters: ! iopt : integer flag. unchanged on exit. ! on entry iopt must specify whether a least-squares surface ! (iopt=-1) or a smoothing surface (iopt=0 or 1)must be ! determined. ! if iopt=0 the routine will start with the initial set of ! knots needed for determining the least-squares polynomial ! surface. ! if iopt=1 the routine will continue with the set of knots ! found at the last call of the routine. ! attention: a call with iopt=1 must always be immediately ! preceded by another call with iopt = 1 or iopt = 0. ! ipar : integer array of dimension 2. unchanged on exit. ! on entry ipar(1) must specify whether (ipar(1)=1) or not ! (ipar(1)=0) the splines must be periodic in the variable u. ! on entry ipar(2) must specify whether (ipar(2)=1) or not ! (ipar(2)=0) the splines must be periodic in the variable v. ! idim : integer. on entry idim must specify the dimension of the ! surface. 1 <= idim <= 3. unchanged on exit. ! mu : integer. on entry mu must specify the number of grid points ! along the u-axis. unchanged on exit. ! mu >= mumin where mumin=4-2*ipar(1) ! u : real array of dimension at least (mu). before entry, u(i) ! must be set to the u-co-ordinate of the i-th grid point ! along the u-axis, for i=1,2,...,mu. these values must be ! supplied in strictly ascending order. unchanged on exit. ! mv : integer. on entry mv must specify the number of grid points ! along the v-axis. unchanged on exit. ! mv >= mvmin where mvmin=4-2*ipar(2) ! v : real array of dimension at least (mv). before entry, v(j) ! must be set to the v-co-ordinate of the j-th grid point ! along the v-axis, for j=1,2,...,mv. these values must be ! supplied in strictly ascending order. unchanged on exit. ! f : real array of dimension at least (mu*mv*idim). ! before entry, f(mu*mv*(l-1)+mv*(i-1)+j) must be set to the ! l-th co-ordinate of the data point corresponding to the ! the grid point (u(i),v(j)) for l=1,...,idim ,i=1,...,mu ! and j=1,...,mv. unchanged on exit. ! if ipar(1)=1 it is expected that f(mu*mv*(l-1)+mv*(mu-1)+j) ! = f(mu*mv*(l-1)+j), l=1,...,idim ; j=1,...,mv ! if ipar(2)=1 it is expected that f(mu*mv*(l-1)+mv*(i-1)+mv) ! = f(mu*mv*(l-1)+mv*(i-1)+1), l=1,...,idim ; i=1,...,mu ! s : real. on entry (if iopt>=0) s must specify the smoothing ! factor. s >=0. unchanged on exit. ! for advice on the choice of s see further comments ! nuest : integer. unchanged on exit. ! nvest : integer. unchanged on exit. ! on entry, nuest and nvest must specify an upper bound for the ! number of knots required in the u- and v-directions respect. ! these numbers will also determine the storage space needed by ! the routine. nuest >= 8, nvest >= 8. ! in most practical situation nuest = mu/2, nvest=mv/2, will ! be sufficient. always large enough are nuest=mu+4+2*ipar(1), ! nvest = mv+4+2*ipar(2), the number of knots needed for ! interpolation (s=0). see also further comments. ! nu : integer. ! unless ier=10 (in case iopt>=0), nu will contain the total ! number of knots with respect to the u-variable, of the spline ! surface returned. if the computation mode iopt=1 is used, ! the value of nu should be left unchanged between subsequent ! calls. in case iopt=-1, the value of nu should be specified ! on entry. ! tu : real array of dimension at least (nuest). ! on successful exit, this array will contain the knots of the ! splines with respect to the u-variable, i.e. the position of ! the interior knots tu(5),...,tu(nu-4) as well as the position ! of the additional knots tu(1),...,tu(4) and tu(nu-3),..., ! tu(nu) needed for the b-spline representation. ! if the computation mode iopt=1 is used,the values of tu(1) ! ...,tu(nu) should be left unchanged between subsequent calls. ! if the computation mode iopt=-1 is used, the values tu(5), ! ...tu(nu-4) must be supplied by the user, before entry. ! see also the restrictions (ier=10). ! nv : integer. ! unless ier=10 (in case iopt>=0), nv will contain the total ! number of knots with respect to the v-variable, of the spline ! surface returned. if the computation mode iopt=1 is used, ! the value of nv should be left unchanged between subsequent ! calls. in case iopt=-1, the value of nv should be specified ! on entry. ! tv : real array of dimension at least (nvest). ! on successful exit, this array will contain the knots of the ! splines with respect to the v-variable, i.e. the position of ! the interior knots tv(5),...,tv(nv-4) as well as the position ! of the additional knots tv(1),...,tv(4) and tv(nv-3),..., ! tv(nv) needed for the b-spline representation. ! if the computation mode iopt=1 is used,the values of tv(1) ! ...,tv(nv) should be left unchanged between subsequent calls. ! if the computation mode iopt=-1 is used, the values tv(5), ! ...tv(nv-4) must be supplied by the user, before entry. ! see also the restrictions (ier=10). ! c : real array of dimension at least (nuest-4)*(nvest-4)*idim. ! on successful exit, c contains the coefficients of the spline ! approximation s(u,v) ! fp : real. unless ier=10, fp contains the sum of squared ! residuals of the spline surface returned. ! wrk : real array of dimension (lwrk). used as workspace. ! if the computation mode iopt=1 is used the values of ! wrk(1),...,wrk(4) should be left unchanged between subsequent ! calls. ! lwrk : integer. on entry lwrk must specify the actual dimension of ! the array wrk as declared in the calling (sub)program. ! lwrk must not be too small. ! lwrk >= 4+nuest*(mv*idim+11+4*ipar(1))+nvest*(11+4*ipar(2))+ ! 4*(mu+mv)+q*idim where q is the larger of mv and nuest. ! iwrk : integer array of dimension (kwrk). used as workspace. ! if the computation mode iopt=1 is used the values of ! iwrk(1),.,iwrk(3) should be left unchanged between subsequent ! calls. ! kwrk : integer. on entry kwrk must specify the actual dimension of ! the array iwrk as declared in the calling (sub)program. ! kwrk >= 3+mu+mv+nuest+nvest. ! ier : integer. unless the routine detects an error, ier contains a ! non-positive value on exit, i.e. ! ier=0 : normal return. the surface returned has a residual sum of ! squares fp such that abs(fp-s)/s <= tol with tol a relat- ! ive tolerance set to 0.001 by the program. ! ier=-1 : normal return. the spline surface returned is an ! interpolating surface (fp=0). ! ier=-2 : normal return. the surface returned is the least-squares ! polynomial surface. in this extreme case fp gives the ! upper bound for the smoothing factor s. ! ier=1 : error. the required storage space exceeds the available ! storage space, as specified by the parameters nuest and ! nvest. ! probably causes : nuest or nvest too small. if these param- ! eters are already large, it may also indicate that s is ! too small ! the approximation returned is the least-squares surface ! according to the current set of knots. the parameter fp ! gives the corresponding sum of squared residuals (fp>s). ! ier=2 : error. a theoretically impossible result was found during ! the iteration process for finding a smoothing surface with ! fp = s. probably causes : s too small. ! there is an approximation returned but the corresponding ! sum of squared residuals does not satisfy the condition ! abs(fp-s)/s < tol. ! ier=3 : error. the maximal number of iterations maxit (set to 20 ! by the program) allowed for finding a smoothing surface ! with fp=s has been reached. probably causes : s too small ! there is an approximation returned but the corresponding ! sum of squared residuals does not satisfy the condition ! abs(fp-s)/s < tol. ! ier=10 : error. on entry, the input data are controlled on validity ! the following restrictions must be satisfied. ! -1<=iopt<=1, 0<=ipar(1)<=1, 0<=ipar(2)<=1, 1 <=idim<=3 ! mu >= 4-2*ipar(1),mv >= 4-2*ipar(2), nuest >=8, nvest >= 8, ! kwrk>=3+mu+mv+nuest+nvest, ! lwrk >= 4+nuest*(mv*idim+11+4*ipar(1))+nvest*(11+4*ipar(2)) ! +4*(mu+mv)+max(nuest,mv)*idim ! u(i-1)<u(i),i=2,..,mu, v(i-1)<v(i),i=2,...,mv ! if iopt=-1: 8<=nu<=min(nuest,mu+4+2*ipar(1)) ! u(1)<tu(5)<tu(6)<...<tu(nu-4)<u(mu) ! 8<=nv<=min(nvest,mv+4+2*ipar(2)) ! v(1)<tv(5)<tv(6)<...<tv(nv-4)<v(mv) ! the schoenberg-whitney conditions, i.e. there must ! be subset of grid co-ordinates uu(p) and vv(q) such ! that tu(p) < uu(p) < tu(p+4) ,p=1,...,nu-4 ! tv(q) < vv(q) < tv(q+4) ,q=1,...,nv-4 ! (see fpchec or fpchep) ! if iopt>=0: s>=0 ! if s=0: nuest>=mu+4+2*ipar(1) ! nvest>=mv+4+2*ipar(2) ! if one of these conditions is found to be violated,control ! is immediately repassed to the calling program. in that ! case there is no approximation returned. ! ! further comments: ! by means of the parameter s, the user can control the tradeoff ! between closeness of fit and smoothness of fit of the approximation. ! if s is too large, the surface will be too smooth and signal will be ! lost ; if s is too small the surface will pick up too much noise. in ! the extreme cases the program will return an interpolating surface ! if s=0 and the constrained least-squares polynomial surface if s is ! very large. between these extremes, a properly chosen s will result ! in a good compromise between closeness of fit and smoothness of fit. ! to decide whether an approximation, corresponding to a certain s is ! satisfactory the user is highly recommended to inspect the fits ! graphically. ! recommended values for s depend on the accuracy of the data values. ! if the user has an idea of the statistical errors on the data, he ! can also find a proper estimate for s. for, by assuming that, if he ! specifies the right s, parsur will return a surface s(u,v) which ! exactly reproduces the surface underlying the data he can evaluate ! the sum(dist(f(i,j)-s(u(i),v(j)))**2) to find a good estimate for s. ! for example, if he knows that the statistical errors on his f(i,j)- ! values is not greater than 0.1, he may expect that a good s should ! have a value not larger than mu*mv*(0.1)**2. ! if nothing is known about the statistical error in f(i,j), s must ! be determined by trial and error, taking account of the comments ! above. the best is then to start with a very large value of s (to ! determine the le-sq polynomial surface and the corresponding upper ! bound fp0 for s) and then to progressively decrease the value of s ! ( say by a factor 10 in the beginning, i.e. s=fp0/10,fp0/100,... ! and more carefully as the approximation shows more detail) to ! obtain closer fits. ! to economize the search for a good s-value the program provides with ! different modes of computation. at the first call of the routine, or ! whenever he wants to restart with the initial set of knots the user ! must set iopt=0. ! if iopt = 1 the program will continue with the knots found at ! the last call of the routine. this will save a lot of computation ! time if parsur is called repeatedly for different values of s. ! the number of knots of the surface returned and their location will ! depend on the value of s and on the complexity of the shape of the ! surface underlying the data. if the computation mode iopt = 1 ! is used, the knots returned may also depend on the s-values at ! previous calls (if these were smaller). therefore, if after a number ! of trials with different s-values and iopt=1,the user can finally ! accept a fit as satisfactory, it may be worthwhile for him to call ! parsur once more with the chosen value for s but now with iopt=0. ! indeed, parsur may then return an approximation of the same quality ! of fit but with fewer knots and therefore better if data reduction ! is also an important objective for the user. ! the number of knots may also depend on the upper bounds nuest and ! nvest. indeed, if at a certain stage in parsur the number of knots ! in one direction (say nu) has reached the value of its upper bound ! (nuest), then from that moment on all subsequent knots are added ! in the other (v) direction. this may indicate that the value of ! nuest is too small. on the other hand, it gives the user the option ! of limiting the number of knots the routine locates in any direction ! for example, by setting nuest=8 (the lowest allowable value for ! nuest), the user can indicate that he wants an approximation with ! splines which are simple cubic polynomials in the variable u. ! ! other subroutines required: ! fppasu,fpchec,fpchep,fpknot,fprati,fpgrpa,fptrnp,fpback, ! fpbacp,fpbspl,fptrpe,fpdisc,fpgivs,fprota ! ! author: ! p.dierckx ! dept. computer science, k.u. leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! latest update : march 1989 ! ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: s real(RKIND), intent(inout) :: fp integer, intent(in) :: iopt,idim,mu,mv,nuest,nvest,lwrk,kwrk integer, intent(inout) :: nu,nv integer, intent(out) :: ier ! ..array arguments.. real(RKIND), intent(in) :: u(mu),v(mv),f(mu*mv*idim) real(RKIND), intent(inout) :: tu(nuest),tv(nvest),c((nuest-4)*(nvest-4)*idim),wrk(lwrk) integer, intent(in) :: ipar(2) integer, intent(inout) :: iwrk(kwrk) ! ..local scalars.. real(RKIND) :: ub,ue,vb,ve,peru,perv integer :: jwrk,kndu,kndv,knru,knrv,kwest,lfpu,lfpv,lwest,lww,nc,mf,mumin,mvmin ! ..subroutine references.. ! fppasu,fpchec,fpchep ! .. ! we set up the parameters tol and maxit. integer, parameter :: maxit = 20 real(RKIND), parameter :: tol = smallnum03 ! before starting computations a data check is made. if the input data ! are invalid, control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR mumin = 4-2*ipar(1) mvmin = 4-2*ipar(2) mf = mu*mv nc = (nuest-4)*(nvest-4) lwest = 4+nuest*(mv*idim+11+4*ipar(1))+nvest*(11+4*ipar(2))+4*(mu+mv)+max(nuest,mv)*idim kwest = 3+mu+mv+nuest+nvest if (iopt<(-1) .or. iopt>1) return if (any(ipar<0 .or. ipar>1)) return if (idim<=0 .or. idim>3) return if (mu<mumin .or. nuest<8) return if (mv<mvmin .or. nvest<8) return if (lwrk<lwest .or. kwrk<kwest) return if (any(u(:mu-1)>=u(2:mu))) return if (any(v(:mv-1)>=v(2:mv))) return if (iopt<0) then if (nu<8 .or. nu>nuest) return ub = u(1) ue = u(mu) u_period: if (ipar(1)==0) then tu(1:4) = ub tu(nu-3:nu) = ue ier = fpchec(u,mu,tu,nu,3) else u_period peru = ue-ub tu(1:4) = [tu(nu-6:nu-4)-peru, ub] tu(nu-3:nu) = [ue, tu(5:7)+peru] ier = fpchep(u,mu,tu,nu,3) endif u_period if (ier/=FITPACK_OK) return if (nv<8 .or. nv>nvest) return vb = v(1) ve = v(mv) v_period: if (ipar(2)==0) then tv(1:4) = vb tv(nv-3:nv) = ve ier = fpchec(v,mv,tv,nv,3) else v_period perv = ve-vb tv(1:4) = [tv(nv-6:nv-4)-perv, vb] tv(nv-3:nv) = [ve, tv(5:7)+perv] ier = fpchep(v,mv,tv,nv,3) endif v_period if (ier/=FITPACK_OK) return else if(s<zero) return if(equal(s,zero) .and. (nuest<(mu+4+2*ipar(1)) .or. & nvest<(mv+4+2*ipar(2))) )return ier = FITPACK_OK endif ! we partition the working space and determine the spline approximation lfpu = 5 lfpv = lfpu+nuest lww = lfpv+nvest jwrk = lwrk-4-nuest-nvest knru = 4 knrv = knru+mu kndu = knrv+mv kndv = kndu+nuest call fppasu(iopt,ipar,idim,u,mu,v,mv,f,mf,s,nuest,nvest, & tol,maxit,nc,nu,tu,nv,tv,c,fp,wrk(1),wrk(2),wrk(3),wrk(4), & wrk(lfpu),wrk(lfpv),iwrk(1),iwrk(2),iwrk(3),iwrk(knru), & iwrk(knrv),iwrk(kndu),iwrk(kndv),wrk(lww),jwrk,ier) return end subroutine parsur ! given the set of data points (x(i),y(i)) and the set of positive numbers w(i),i=1,2,...,m-1, ! subroutine percur determines a smooth periodic spline approximation of degree k with period ! per=x(m)-x(1). ! iopt=-1: calculate the weighted least-squares periodic spline according to a given set of knots. ! iopt>=0: the number of knots of the spline s(x) and the position t(j),j=1,2,...,n is chosen ! automatically by the routine. the smoothness of s(x) is then achieved by minimalizing ! the discontinuity jumps of the k-th derivative of s(x) at the knots t(j),j=k+2,k+3,..., ! n-k-1. the amount of smoothness is determined by the condition that ! f(p)=sum((w(i)*(y(i)-s(x(i))))**2) be <= s, with s a given nonnegative constant, called ! the smoothing factor. ! the fit s(x) is given in the b-spline representation (b-spline coefficients c(j),j=1:n-k-1) and ! can be evaluated by means of subroutine splev. pure subroutine percur(iopt,m,x,y,w,k,s,nest,n,t,c,fp,wrk,lwrk,iwrk,ier) ! calling sequence: ! call percur(iopt,m,x,y,w,k,s,nest,n,t,c,fp,wrk,lwrk,iwrk,ier) ! ! parameters: ! iopt : integer flag. on entry iopt must specify whether a weighted least-squares spline (iopt ! =-1) or a smoothing spline (iopt=0 or 1) must be determined. if iopt=0 the routine will ! start with an initial set of knots t(i)=x(1)+(x(m)-x(1))*(i-k-1), i=1:2*k+2. ! if iopt=1 the routine will continue with the knots found at the last call of the ! routine. caution: a call with iopt=1 must always be immediately preceded by another ! call with iopt=1 or iopt=0. unchanged on exit. ! m : integer. on entry m must specify the number of data points. m > 1. unchanged on exit. ! x : real array of dimension at least (m). before entry, x(i) must be set to the i-th value ! of the independent variable x, for i=1:m. these values must be supplied in strictly ! ascending order. x(m) only indicates the length of the period of the spline, i.e ! per=x(m)-x(1). unchanged on exit. ! y : real array of dimension at least (m). before entry, y(i) must be set to the i-th value ! of the dependent variable y, for i=1,2,...,m-1. the element y(m) is not used. ! unchanged on exit. ! w : real array of dimension at least (m). before entry, w(i) must be set to the i-th value ! in the set of weights. the w(i) must be strictly positive. w(m) is not used. ! see also further comments. unchanged on exit. ! k : integer. on entry k must specify the degree of the spline, 1<=k<=5. it is recommended ! to use cubic splines (k=3). the user is strongly dissuaded from choosing k even, ! together with a small s-value. unchanged on exit. ! s : real.on entry (in case iopt>=0) s must specify the smoothing factor. s >=0. unchanged ! on exit. for advice on the choice of s see further comments. ! nest : integer. on entry nest must contain an over-estimate of the total number of knots of ! the spline returned, to indicate the storage space available to the routine. nest ! >=2*k+2. in most practical situation nest=m/2 will be sufficient. always large enough ! is nest=m+2*k,the number of knots needed for interpolation (s=0). unchanged on exit. ! n : unless ier = 10 (in case iopt >=0), n will contain the total number of knots of the ! spline approximation returned. if the computation mode iopt=1 is used this value of n ! should be left unchanged between subsequent calls. in case iopt=-1, the value of n must ! be specified on entry. ! t : real array of dimension at least (nest). ! on successful exit, this array will contain the knots of the spline,i.e. the position ! of the interior knots t(k+2:n-k-1) as well as the position of the additional knots ! t(1:k+1)=x(1) and t(n-k)=x(m),..,t(n) needed for the b-spline representation. ! if the computation mode iopt=1 is used, the values of t(1), t(2:n) should be left ! unchanged between subsequent calls. if the computation mode iopt=-1 is used, the values ! t(k+2),...,t(n-k-1) must be supplied by the user, before entry. see also the ! restrictions (ier=10). ! c : real array of dimension at least (nest). on successful exit, this array will contain ! the coefficients c(1:n-k-1) in the b-spline representation of s(x) ! fp : real. unless ier = 10, fp contains the weighted sum of squared residuals of the spline ! approximation returned. ! wrk : real array of dimension at least (m*(k+1)+nest*(8+5*k)). used as working space. if the ! computation mode iopt=1 is used, the values wrk(1),...,wrk(n) should be left unchanged ! between subsequent calls. ! lwrk : integer. on entry,lwrk must specify the actual dimension of the array wrk as declared ! in the calling (sub)program. lwrk must not be too small (see wrk). unchanged on exit. ! iwrk : integer array of dimension at least (nest). used as working space. if the computation ! mode iopt=1 is used,the values iwrk(1),...,iwrk(n) should be left unchanged between ! subsequent calls. ! ier : integer. output error flag ! ier=10 : error. onon input, the following constraints must be satisfied. ! -1<=iopt<=1, 1<=k<=5, m>1, nest>2*k+2, w(i)>0,i=1,...,m-1 ! x(1)<x(2)<...<x(m), lwrk>=(k+1)*m+nest*(8+5*k) ! if iopt=-1: 2*k+2<=n<=min(nest,m+2*k) ! x(1)<t(k+2)<t(k+3)<...<t(n-k-1)<x(m) ! the schoenberg-whitney conditions, i.e. there ! must be a subset of data points xx(j) with ! xx(j) = x(i) or x(i)+(x(m)-x(1)) such that ! t(j) < xx(j) < t(j+k+1), j=k+1,...,n-k-1 ! if iopt>=0: s>=0 ! if s=0 : nest >= m+2*k ! if one of these conditions is found to be violated,control is immediately repassed ! to the calling program. in that case there is no approximation returned. ! ! further comments: ! by means of the parameter s, the user can control the tradeoff between closeness of fit and ! smoothness of fit of the approximation. if s is too large, the spline will be too smooth and ! signal will be lost ; if s is too small the spline will pick up too much noise. in the extreme ! cases the program will return an interpolating periodic spline if s=0 and the weighted least- ! squares constant if s is very large. between these extremes, a properly chosen s will result in ! a good compromise between closeness of fit and smoothness of fit. to decide whether an ! approximation, corresponding to a certain s is satisfactory the user is highly recommended to ! inspect the fits graphically. ! recommended values for s depend on the weights w(i). if these are taken as 1/d(i) with d(i) an ! estimate of the standard deviation of y(i), a good s-value should be found in the range ! (m-sqrt(2*m),m+sqrt(2*m)). if nothing is known about the statistical error in y(i) each w(i) ! can be set equal to one and s determined by trial and error, taking account of the comments ! above. the best is then to start with a very large value of s ( to determine the least-squares ! constant and the corresponding upper bound fp0 for s) and then to progressively decrease the ! value of s ( say by a factor 10 in the beginning, i.e. s=fp0/10, fp0/100,...and more carefully ! as the approximation shows more detail) to obtain closer fits. to economize the search for a ! good s-value the program provides with different modes of computation. at the first call of the ! routine, or whenever he wants to restart with the initial set of knots the user must set iopt=0. ! ! if iopt=1 the program will continue with the set of knots found at the last call of the routine. ! this will save a lot of computation time if percur is called repeatedly for different values of ! s. the number of knots of the spline returned and their location will depend on the value of s ! and on the complexity of the shape of the function underlying the data. but, if the computation ! mode iopt=1 is used, the knots returned may also depend on the s-values at previous calls (if ! these were smaller). therefore, if after a number of trials with different s-values and iopt=1, ! the user can finally accept a fit as satisfactory, it may be worthwhile for him to call percur ! once more with the selected value for s but now with iopt=0. indeed, percur may then return an ! approximation of the same quality of fit but with fewer knots and therefore better if data ! reduction is also an important objective for the user. ! ! other subroutines required: ! fpbacp,fpbspl,fpchep,fpperi,fpdisc,fpgivs,fpknot,fprati,fprota ! ! references: ! dierckx p. : algorithms for smoothing data with periodic and parametric splines, computer ! graphics and image processing 20 (1982) 171-184. ! dierckx p. : algorithms for smoothing data with periodic and parametric splines, report tw55, ! dept. computer science, k.u.leuven, 1981. ! dierckx p. : curve and surface fitting with splines, monographs on numerical analysis, oxford ! university press, 1993. ! ! author: ! p.dierckx ! dept. computer science, k.u. leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! creation date : may 1979 ! ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: s real(RKIND), intent(inout) :: fp integer, intent(inout) :: n,ier integer, intent(in) :: iopt,m,k,nest,lwrk ! ..array arguments.. real(RKIND), intent(in) :: x(m),y(m),w(m) real(RKIND), intent(inout) :: t(nest),c(nest),wrk(lwrk) integer, intent(inout) :: iwrk(nest) ! ..local scalars.. real(RKIND) :: per integer :: i,ia1,ia2,ib,ifp,ig1,ig2,iq,iz,i1,i2,j1,j2,k1,k2,lwest,m1,nmin ! .. ! we set up the parameters tol and maxit integer, parameter :: maxit = 20 real(RKIND), parameter :: tol = smallnum03 ! before starting computations a data check is made. if the input data ! are invalid, control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR k1 = k+1 k2 = k1+1 nmin = 2*k1 m1 = m-1 lwest = m*k1+nest*(8+5*k) if (k<=0 .or. k>5) return if (iopt<(-1) .or. iopt>1) return if (m<2 .or. nest<nmin) return if (lwrk<lwest) return if (any(w(:m1)<=zero)) return if (any(x(:m1)>=x(2:m))) return if (iopt>=0) then if (s<zero) return if (equal(s,zero) .and. nest<(m+2*k)) return else if (n<=nmin .or. n>nest) return per = x(m)-x(1) j1 = k1 t(j1) = x(1) i1 = n-k t(i1) = x(m) j2 = j1 i2 = i1 do i=1,k i1 = i1+1 i2 = i2-1 j1 = j1+1 j2 = j2-1 t(j2) = t(i2)-per t(i1) = t(j1)+per end do ier = fpchep(x,m,t,n,k) if (ier/=FITPACK_OK) return endif ier = FITPACK_OK ! we partition the working space and determine the spline approximation. ifp = 1 iz = ifp+nest ia1 = iz+nest ia2 = ia1+nest*k1 ib = ia2+nest*k ig1 = ib+nest*k2 ig2 = ig1+nest*k2 iq = ig2+nest*k1 call fpperi(iopt,x,y,w,m,k,s,nest,tol,maxit,k1,k2,n,t,c,fp, & wrk(ifp),wrk(iz),wrk(ia1),wrk(ia2),wrk(ib),wrk(ig1),wrk(ig2),wrk(iq),iwrk,ier) return end subroutine percur subroutine pogrid(iopt,ider,mu,u,mv,v,z,z0,r,s, & nuest,nvest,nu,tu,nv,tv,c,fp,wrk,lwrk,iwrk,kwrk,ier) ! subroutine pogrid fits a function f(x,y) to a set of data points ! z(i,j) given at the nodes (x,y)=(u(i)*cos(v(j)),u(i)*sin(v(j))), ! i=1,...,mu ; j=1,...,mv , of a radius-angle grid over a disc ! x ** 2 + y ** 2 <= r ** 2 . ! ! this approximation problem is reduced to the determination of a ! bicubic spline s(u,v) smoothing the data (u(i),v(j),z(i,j)) on the ! rectangle 0<=u<=r, v(1)<=v<=v(1)+2*pi ! in order to have continuous partial derivatives ! i+j ! d f(0,0) ! g(i,j) = ---------- ! i j ! dx dy ! ! s(u,v)=f(x,y) must satisfy the following conditions ! ! (1) s(0,v) = g(0,0) v(1)<=v<= v(1)+2*pi ! ! d s(0,v) ! (2) -------- = cos(v)*g(1,0)+sin(v)*g(0,1) v(1)<=v<= v(1)+2*pi ! d u ! ! moreover, s(u,v) must be periodic in the variable v, i.e. ! ! j j ! d s(u,vb) d s(u,ve) ! (3) ---------- = --------- 0 <=u<= r, j=0,1,2 , vb=v(1), ! j j ve=vb+2*pi ! d v d v ! ! the number of knots of s(u,v) and their position tu(i),i=1,2,...,nu; ! tv(j),j=1,2,...,nv, is chosen automatically by the routine. the ! smoothness of s(u,v) is achieved by minimalizing the discontinuity ! jumps of the derivatives of the spline at the knots. the amount of ! smoothness of s(u,v) is determined by the condition that ! fp=sumi=1,mu(sumj=1,mv((z(i,j)-s(u(i),v(j)))**2))+(z0-g(0,0))**2<=s, ! with s a given non-negative constant. ! the fit s(u,v) is given in its b-spline representation and can be ! evaluated by means of routine bispev. f(x,y) = s(u,v) can also be ! evaluated by means of function program evapol. ! ! calling sequence: ! call pogrid(iopt,ider,mu,u,mv,v,z,z0,r,s,nuest,nvest,nu,tu, ! * ,nv,tv,c,fp,wrk,lwrk,iwrk,kwrk,ier) ! ! parameters: ! iopt : integer array of dimension 3, specifying different options. ! unchanged on exit. ! iopt(1):on entry iopt(1) must specify whether a least-squares spline ! (iopt(1)=-1) or a smoothing spline (iopt(1)=0 or 1) must be ! determined. ! if iopt(1)=0 the routine will start with an initial set of ! knots tu(i)=0,tu(i+4)=r,i=1,...,4;tv(i)=v(1)+(i-4)*2*pi,i=1,. ! ...,8. ! if iopt(1)=1 the routine will continue with the set of knots ! found at the last call of the routine. ! attention: a call with iopt(1)=1 must always be immediately ! preceded by another call with iopt(1) = 1 or iopt(1) = 0. ! iopt(2):on entry iopt(2) must specify the requested order of conti- ! nuity for f(x,y) at the origin. ! if iopt(2)=0 only condition (1) must be fulfilled and ! if iopt(2)=1 conditions (1)+(2) must be fulfilled. ! iopt(3):on entry iopt(3) must specify whether (iopt(3)=1) or not ! (iopt(3)=0) the approximation f(x,y) must vanish at the ! boundary of the approximation domain. ! ider : integer array of dimension 2, specifying different options. ! unchanged on exit. ! ider(1):on entry ider(1) must specify whether (ider(1)=0 or 1) or not ! (ider(1)=-1) there is a data value z0 at the origin. ! if ider(1)=1, z0 will be considered to be the right function ! value, and it will be fitted exactly (g(0,0)=z0=c(1)). ! if ider(1)=0, z0 will be considered to be a data value just ! like the other data values z(i,j). ! ider(2):on entry ider(2) must specify whether (ider(2)=1) or not ! (ider(2)=0) f(x,y) must have vanishing partial derivatives ! g(1,0) and g(0,1) at the origin. (in case iopt(2)=1) ! mu : integer. on entry mu must specify the number of grid points ! along the u-axis. unchanged on exit. ! mu >= mumin where mumin=4-iopt(3)-ider(2) if ider(1)<0 ! =3-iopt(3)-ider(2) if ider(1)>=0 ! u : real array of dimension at least (mu). before entry, u(i) ! must be set to the u-co-ordinate of the i-th grid point ! along the u-axis, for i=1,2,...,mu. these values must be ! positive and supplied in strictly ascending order. ! unchanged on exit. ! mv : integer. on entry mv must specify the number of grid points ! along the v-axis. mv > 3 . unchanged on exit. ! v : real array of dimension at least (mv). before entry, v(j) ! must be set to the v-co-ordinate of the j-th grid point ! along the v-axis, for j=1,2,...,mv. these values must be ! supplied in strictly ascending order. unchanged on exit. ! -pi <= v(1) < pi , v(mv) < v(1)+2*pi. ! z : real array of dimension at least (mu*mv). ! before entry, z(mv*(i-1)+j) must be set to the data value at ! the grid point (u(i),v(j)) for i=1,...,mu and j=1,...,mv. ! unchanged on exit. ! z0 : real value. on entry (if ider(1) >=0 ) z0 must specify the ! data value at the origin. unchanged on exit. ! r : real value. on entry r must specify the radius of the disk. ! r>=u(mu) (>u(mu) if iopt(3)=1). unchanged on exit. ! s : real. on entry (if iopt(1)>=0) s must specify the smoothing ! factor. s >=0. unchanged on exit. ! for advice on the choice of s see further comments ! nuest : integer. unchanged on exit. ! nvest : integer. unchanged on exit. ! on entry, nuest and nvest must specify an upper bound for the ! number of knots required in the u- and v-directions respect. ! these numbers will also determine the storage space needed by ! the routine. nuest >= 8, nvest >= 8. ! in most practical situation nuest = mu/2, nvest=mv/2, will ! be sufficient. always large enough are nuest=mu+5+iopt(2)+ ! iopt(3), nvest = mv+7, the number of knots needed for ! interpolation (s=0). see also further comments. ! nu : integer. ! unless ier=10 (in case iopt(1)>=0), nu will contain the total ! number of knots with respect to the u-variable, of the spline ! approximation returned. if the computation mode iopt(1)=1 is ! used, the value of nu should be left unchanged between sub- ! sequent calls. in case iopt(1)=-1, the value of nu should be ! specified on entry. ! tu : real array of dimension at least (nuest). ! on successful exit, this array will contain the knots of the ! spline with respect to the u-variable, i.e. the position of ! the interior knots tu(5),...,tu(nu-4) as well as the position ! of the additional knots tu(1)=...=tu(4)=0 and tu(nu-3)=...= ! tu(nu)=r needed for the b-spline representation. ! if the computation mode iopt(1)=1 is used,the values of tu(1) ! ...,tu(nu) should be left unchanged between subsequent calls. ! if the computation mode iopt(1)=-1 is used, the values tu(5), ! ...tu(nu-4) must be supplied by the user, before entry. ! see also the restrictions (ier=10). ! nv : integer. ! unless ier=10 (in case iopt(1)>=0), nv will contain the total ! number of knots with respect to the v-variable, of the spline ! approximation returned. if the computation mode iopt(1)=1 is ! used, the value of nv should be left unchanged between sub- ! sequent calls. in case iopt(1) = -1, the value of nv should ! be specified on entry. ! tv : real array of dimension at least (nvest). ! on successful exit, this array will contain the knots of the ! spline with respect to the v-variable, i.e. the position of ! the interior knots tv(5),...,tv(nv-4) as well as the position ! of the additional knots tv(1),...,tv(4) and tv(nv-3),..., ! tv(nv) needed for the b-spline representation. ! if the computation mode iopt(1)=1 is used,the values of tv(1) ! ...,tv(nv) should be left unchanged between subsequent calls. ! if the computation mode iopt(1)=-1 is used, the values tv(5), ! ...tv(nv-4) must be supplied by the user, before entry. ! see also the restrictions (ier=10). ! c : real array of dimension at least (nuest-4)*(nvest-4). ! on successful exit, c contains the coefficients of the spline ! approximation s(u,v) ! fp : real. unless ier=10, fp contains the sum of squared ! residuals of the spline approximation returned. ! wrk : real array of dimension (lwrk). used as workspace. ! if the computation mode iopt(1)=1 is used the values of ! wrk(1),...,wrk(8) should be left unchanged between subsequent ! calls. ! lwrk : integer. on entry lwrk must specify the actual dimension of ! the array wrk as declared in the calling (sub)program. ! lwrk must not be too small. ! lwrk >= 8+nuest*(mv+nvest+3)+nvest*21+4*mu+6*mv+q ! where q is the larger of (mv+nvest) and nuest. ! iwrk : integer array of dimension (kwrk). used as workspace. ! if the computation mode iopt(1)=1 is used the values of ! iwrk(1),.,iwrk(4) should be left unchanged between subsequent ! calls. ! kwrk : integer. on entry kwrk must specify the actual dimension of ! the array iwrk as declared in the calling (sub)program. ! kwrk >= 4+mu+mv+nuest+nvest. ! ier : integer. unless the routine detects an error, ier contains a ! non-positive value on exit, i.e. ! ier=0 : normal return. the spline returned has a residual sum of ! squares fp such that abs(fp-s)/s <= tol with tol a relat- ! ive tolerance set to 0.001 by the program. ! ier=-1 : normal return. the spline returned is an interpolating ! spline (fp=0). ! ier=-2 : normal return. the spline returned is the least-squares ! constrained polynomial. in this extreme case fp gives the ! upper bound for the smoothing factor s. ! ier=1 : error. the required storage space exceeds the available ! storage space, as specified by the parameters nuest and ! nvest. ! probably causes : nuest or nvest too small. if these param- ! eters are already large, it may also indicate that s is ! too small ! the approximation returned is the least-squares spline ! according to the current set of knots. the parameter fp ! gives the corresponding sum of squared residuals (fp>s). ! ier=2 : error. a theoretically impossible result was found during ! the iteration process for finding a smoothing spline with ! fp = s. probably causes : s too small. ! there is an approximation returned but the corresponding ! sum of squared residuals does not satisfy the condition ! abs(fp-s)/s < tol. ! ier=3 : error. the maximal number of iterations maxit (set to 20 ! by the program) allowed for finding a smoothing spline ! with fp=s has been reached. probably causes : s too small ! there is an approximation returned but the corresponding ! sum of squared residuals does not satisfy the condition ! abs(fp-s)/s < tol. ! ier=10 : error. on entry, the input data are controlled on validity ! the following restrictions must be satisfied. ! -1<=iopt(1)<=1, 0<=iopt(2)<=1, 0<=iopt(3)<=1, ! -1<=ider(1)<=1, 0<=ider(2)<=1, ider(2)=0 if iopt(2)=0. ! mu >= mumin (see above), mv >= 4, nuest >=8, nvest >= 8, ! kwrk>=4+mu+mv+nuest+nvest, ! lwrk >= 8+nuest*(mv+nvest+3)+nvest*21+4*mu+6*mv+ ! max(nuest,mv+nvest) ! 0< u(i-1)<u(i)<=r,i=2,..,mu, (< r if iopt(3)=1) ! -pi<=v(1)< pi, v(1)<v(i-1)<v(i)<v(1)+2*pi, i=3,...,mv ! if iopt(1)=-1: 8<=nu<=min(nuest,mu+5+iopt(2)+iopt(3)) ! 0<tu(5)<tu(6)<...<tu(nu-4)<r ! 8<=nv<=min(nvest,mv+7) ! v(1)<tv(5)<tv(6)<...<tv(nv-4)<v(1)+2*pi ! the schoenberg-whitney conditions, i.e. there must ! be subset of grid co-ordinates uu(p) and vv(q) such ! that tu(p) < uu(p) < tu(p+4) ,p=1,...,nu-4 ! (iopt(2)=1 and iopt(3)=1 also count for a uu-value ! tv(q) < vv(q) < tv(q+4) ,q=1,...,nv-4 ! (vv(q) is either a value v(j) or v(j)+2*pi) ! if iopt(1)>=0: s>=0 ! if s=0: nuest>=mu+5+iopt(2)+iopt(3), nvest>=mv+7 ! if one of these conditions is found to be violated,control ! is immediately repassed to the calling program. in that ! case there is no approximation returned. ! ! further comments: ! pogrid does not allow individual weighting of the data-values. ! so, if these were determined to widely different accuracies, then ! perhaps the general data set routine polar should rather be used ! in spite of efficiency. ! by means of the parameter s, the user can control the tradeoff ! between closeness of fit and smoothness of fit of the approximation. ! if s is too large, the spline will be too smooth and signal will be ! lost ; if s is too small the spline will pick up too much noise. in ! the extreme cases the program will return an interpolating spline if ! s=0 and the constrained least-squares polynomial(degrees 3,0)if s is ! very large. between these extremes, a properly chosen s will result ! in a good compromise between closeness of fit and smoothness of fit. ! to decide whether an approximation, corresponding to a certain s is ! satisfactory the user is highly recommended to inspect the fits ! graphically. ! recommended values for s depend on the accuracy of the data values. ! if the user has an idea of the statistical errors on the data, he ! can also find a proper estimate for s. for, by assuming that, if he ! specifies the right s, pogrid will return a spline s(u,v) which ! exactly reproduces the function underlying the data he can evaluate ! the sum((z(i,j)-s(u(i),v(j)))**2) to find a good estimate for this s ! for example, if he knows that the statistical errors on his z(i,j)- ! values is not greater than 0.1, he may expect that a good s should ! have a value not larger than mu*mv*(0.1)**2. ! if nothing is known about the statistical error in z(i,j), s must ! be determined by trial and error, taking account of the comments ! above. the best is then to start with a very large value of s (to ! determine the least-squares polynomial and the corresponding upper ! bound fp0 for s) and then to progressively decrease the value of s ! ( say by a factor 10 in the beginning, i.e. s=fp0/10,fp0/100,... ! and more carefully as the approximation shows more detail) to ! obtain closer fits. ! to economize the search for a good s-value the program provides with ! different modes of computation. at the first call of the routine, or ! whenever he wants to restart with the initial set of knots the user ! must set iopt(1)=0. ! if iopt(1) = 1 the program will continue with the knots found at ! the last call of the routine. this will save a lot of computation ! time if pogrid is called repeatedly for different values of s. ! the number of knots of the spline returned and their location will ! depend on the value of s and on the complexity of the shape of the ! function underlying the data. if the computation mode iopt(1) = 1 ! is used, the knots returned may also depend on the s-values at ! previous calls (if these were smaller). therefore, if after a number ! of trials with different s-values and iopt(1)=1,the user can finally ! accept a fit as satisfactory, it may be worthwhile for him to call ! pogrid once more with the chosen value for s but now with iopt(1)=0. ! indeed, pogrid may then return an approximation of the same quality ! of fit but with fewer knots and therefore better if data reduction ! is also an important objective for the user. ! the number of knots may also depend on the upper bounds nuest and ! nvest. indeed, if at a certain stage in pogrid the number of knots ! in one direction (say nu) has reached the value of its upper bound ! (nuest), then from that moment on all subsequent knots are added ! in the other (v) direction. this may indicate that the value of ! nuest is too small. on the other hand, it gives the user the option ! of limiting the number of knots the routine locates in any direction ! for example, by setting nuest=8 (the lowest allowable value for ! nuest), the user can indicate that he wants an approximation which ! is a simple cubic polynomial in the variable u. ! ! other subroutines required: ! fppogr,fpchec,fpchep,fpknot,fpopdi,fprati,fpgrdi,fpsysy,fpback, ! fpbacp,fpbspl,fpcyt1,fpcyt2,fpdisc,fpgivs,fprota ! ! references: ! dierckx p. : fast algorithms for smoothing data over a disc or a ! sphere using tensor product splines, in "algorithms ! for approximation", ed. j.c.mason and m.g.cox, ! clarendon press oxford, 1987, pp. 51-65 ! dierckx p. : fast algorithms for smoothing data over a disc or a ! sphere using tensor product splines, report tw73, dept. ! computer science,k.u.leuven, 1985. ! dierckx p. : curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author: ! p.dierckx ! dept. computer science, k.u. leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! creation date : july 1985 ! latest update : march 1989 ! ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: z0,r,s real(RKIND), intent(inout) :: fp integer, intent(in) :: mu,mv,nuest,nvest,lwrk,kwrk integer, intent(out) :: ier integer, intent(inout) :: nu,nv ! ..array arguments.. integer, intent(in) :: iopt(3),ider(2) integer, intent(inout) :: iwrk(kwrk) real(RKIND), intent(in) :: u(mu),v(mv),z(mu*mv) real(RKIND), intent(inout) :: c((nuest-4)*(nvest-4)),tu(nuest),tv(nvest),wrk(lwrk) ! ..local scalars.. real(RKIND) :: uu,ve,zmax,zmin,zb integer :: jwrk,kndu,kndv,knru,knrv,kwest,l,ldz,lfpu,lfpv,lwest,lww,m,mumin,muu,nc ! set constants integer, parameter :: maxit = 20 real(RKIND), parameter :: tol = smallnum03 real(RKIND), parameter :: per = pi2 ve = v(1)+per ! we set up the parameters tol and maxit. ! before starting computations, a data check is made. if the input data ! are invalid, control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR mumin = 4-iopt(3)-ider(2) - merge(1,0,ider(1)>=0) m = mu*mv nc = (nuest-4)*(nvest-4) lwest = 8+nuest*(mv+nvest+3)+21*nvest+4*mu+6*mv+max(nuest,mv+nvest) kwest = 4+mu+mv+nuest+nvest if (iopt(1)<(-1) .or. iopt(1)>1) return if (iopt(2)<0 .or. iopt(2)>1) return if (iopt(3)<0 .or. iopt(3)>1) return if (ider(1)<(-1) .or. ider(1)>1) return if (ider(2)<0 .or. ider(2)>1) return if (ider(2)==1 .and. iopt(2)==0) return if (mu<mumin .or. mv<4) return if (nuest<8 .or. nvest<8) return if (lwrk<lwest .or. kwrk<kwest) return if (u(1)<=zero .or. u(mu)>r) return if (iopt(3)/=0 .and. equal(u(mu),r)) return if (mu>1) then if (any(u(1:mu-1)>=u(2:mu))) return endif if (v(1)<(-pi) .or. v(1)>=pi) return if (v(mv)>=v(1)+per) return if (any(v(1:mv-1)>=v(2:mv))) return if (iopt(1)<=0) then ! if not given, we compute an estimate for z0. if (ider(1)<0) then zb = sum(z(1:mv))/mv else zb = z0 end if ! we determine the range of z-values. zmin = min(zb,minval(z(:m))) zmax = max(zb,maxval(z(:m))) wrk(5) = zb wrk(6) = zero wrk(7) = zero wrk(8) = zmax -zmin iwrk(4) = mu endif if (iopt(1)<0) then if (nu<8 .or. nu>nuest) return if (nv<11 .or. nv>nvest) return tu(1:4) = zero tu(nu-3:nu) = r l = 9 wrk(l) = zero if (iopt(2)/=0) then uu = min(u(1),tu(5)) l = l+1 wrk(l) = uu*half endif wrk(l+1:l+mu) = u l = l+mu if (iopt(3)/=0) then l = l+1 wrk(l) = r endif muu = l-8 ier = fpchec(wrk(9),muu,tu,nu,3); if(ier/=FITPACK_OK) return tv(1:4) = [tv(nv-6:nv-4)-per, v(1)] tv(nv-3:nv) = [ve, tv(5:7)+per] l = 9 wrk(l:l+mv) = [v,ve] ier = fpchep(wrk(9),mv+1,tv,nv,3) if (ier/=FITPACK_OK) return endif if (iopt(1)>=0) then if (s<zero) return if (equal(s,zero) .and. (nuest<(mu+5+iopt(2)+iopt(3)) .or. nvest<(mv+7)) ) return endif ! we partition the working space and determine the spline approximation ldz = 5 lfpu = 9 lfpv = lfpu+nuest lww = lfpv+nvest jwrk = lwrk-8-nuest-nvest knru = 5 knrv = knru+mu kndu = knrv+mv kndv = kndu+nuest call fppogr(iopt,ider,u,mu,v,mv,z,m,zb,r,s,nuest,nvest,tol,maxit, & nc,nu,tu,nv,tv,c,fp,wrk(1),wrk(2),wrk(3),wrk(4),wrk(lfpu), & wrk(lfpv),wrk(ldz),wrk(8),iwrk(1),iwrk(2),iwrk(3),iwrk(4), & iwrk(knru),iwrk(knrv),iwrk(kndu),iwrk(kndv),wrk(lww),jwrk,ier) return end subroutine pogrid ! subroutine polar fits a smooth function f(x,y) to a set of data points (x(i),y(i),z(i)) scattered ! arbitrarily over an approximation domain x**2+y**2 <= rad(atan(y/x))**2. through the transform ! x = u*rad(v)*cos(v) , y = u*rad(v)*sin(v) ! the approximation problem is reduced to the determination of a bi-cubic spline s(u,v) fitting a ! corresponding set of data points (u(i),v(i),z(i)) on the rectangle 0<=u<=1,-pi<=v<=pi. pure subroutine polar(iopt,m,x,y,z,w,rad,s,nuest,nvest, & eps,nu,tu,nv,tv,u,v,c,fp,wrk1,lwrk1,wrk2,lwrk2,iwrk,kwrk,ier) ! in order to have continuous partial derivatives ! i+j ! d f(0,0) ! g(i,j) = ---------- ! i j ! dx dy ! ! s(u,v)=f(x,y) must satisfy the following conditions ! ! (1) s(0,v) = g(0,0) -pi <=v<= pi. ! ! d s(0,v) ! (2) -------- = rad(v)*(cos(v)*g(1,0)+sin(v)*g(0,1)) ! d u ! -pi <=v<= pi ! 2 ! d s(0,v) 2 2 2 ! (3) -------- = rad(v)*(cos(v)*g(2,0)+sin(v)*g(0,2)+sin(2*v)*g(1,1)) ! 2 ! d u -pi <=v<= pi ! ! moreover, s(u,v) must be periodic in the variable v, i.e. ! ! j j ! d s(u,-pi) d s(u,pi) ! (4) ---------- = --------- 0 <=u<= 1, j=0,1,2 ! j j ! d v d v ! ! if iopt(1) < 0 circle calculates a weighted least-squares spline according to a given set of ! knots in u- and v- direction. ! if iopt(1) >=0, the number of knots in each direction and their position tu(j),j=1,2,...,nu ; ! tv(j),j=1,2,...,nv are chosen automatically by the routine. the smoothness of s(u,v) is then ! achieved by minimizing the discontinuity jumps of the derivatives of the spline at the knots. ! the amount of smoothness of s(u,v) is determined by the condition that ! fp = sum((w(i)*(z(i)-s(u(i),v(i))))**2) be <= s, with s a given non-negative constant. ! the bicubic spline is given in its standard b-spline representation and the corresponding ! function f(x,y) can be evaluated by means of function program evapol. ! ! calling sequence: ! call polar(iopt,m,x,y,z,w,rad,s,nuest,nvest,eps,nu,tu, & ! nv,tv,u,v,wrk1,lwrk1,wrk2,lwrk2,iwrk,kwrk,ier) ! ! parameters: ! iopt : integer array of dimension 3, specifying different options. unchanged on exit. ! iopt(1):on entry iopt(1) must specify whether a weighted least-squares polar spline (iopt(1)=-1) ! or a smoothing polar spline (iopt(1)=0 or 1) must be determined. ! if iopt(1)=0 the routine will start with an initial set of knots tu(i)=0,tu(i+4)=1,i=1:4; ! tv(i)=(2*i-9)*pi,i=1:8. if iopt(1)=1 the routine will continue with the set of knots ! found at the last call of the routine. ! attention: a call with iopt(1)=1 must always be immediately preceded by another call with ! iopt(1) = 1 or iopt(1) = 0. ! iopt(2):on entry iopt(2) must specify the requested order of conti nuity for f(x,y) at the origin. ! if iopt(2)=0 only condition (1) must be fulfilled, ! if iopt(2)=1 conditions (1)+(2) must be fulfilled and ! if iopt(2)=2 conditions (1)+(2)+(3) must be fulfilled. ! iopt(3):on entry iopt(3) must specify whether (iopt(3)=1) or not (iopt(3)=0) the approximation ! f(x,y) must vanish at the boundary of the approximation domain. ! m : integer. on entry m must specify the number of data points. ! m >= 4-iopt(2)-iopt(3) unchanged on exit. ! x : real array of dimension at least (m). ! y : real array of dimension at least (m). ! z : real array of dimension at least (m). ! before entry, x(i),y(i),z(i) must be set to the co-ordinates of the i-th data point, ! for i=1,...,m. the order of the data points is immaterial. unchanged on exit. ! w : real array of dimension at least (m). before entry, w(i) must be set to the i-th value in ! the set of weights. the w(i) must be strictly positive. unchanged on exit. ! rad : real function subprogram defining the boundary of the approximation domain, i.e ! x = rad(v)*cos(v) , y = rad(v)*sin(v), -pi <= v <= pi. ! must be declared external in the calling (sub)program. ! s : real. on entry (in case iopt(1) >=0) s must specify the smoothing factor. s >=0. ! unchanged on exit. for advice on the choice of s see further comments ! nuest : integer. unchanged on exit. ! nvest : integer. unchanged on exit. ! on entry, nuest and nvest must specify an upper bound for the number of knots required in ! the u- and v-directions resp. these numbers will also determine the storage space needed ! by the routine. nuest >= 8, nvest >= 8. in most practical situation ! nuest = nvest = 8+sqrt(m/2) will be sufficient. see also further comments. ! eps : real. ! on entry, eps must specify a threshold for determining the effective rank of an over- ! determined linear system of equations. 0 < eps < 1. if the number of decimal digits in ! the computer representation of a real number is q, then 10**(-q) is a suitable value for ! eps in most practical applications. unchanged on exit. ! nu : integer. ! unless ier=10 (in case iopt(1) >=0),nu will contain the total number of knots with respect ! to the u-variable, of the spline approximation returned. if the computation mode iopt(1)=1 ! is used, the value of nu should be left unchanged between subsequent calls. ! in case iopt(1)=-1,the value of nu must be specified on entry ! tu : real array of dimension at least nuest. ! on successful exit, this array will contain the knots of the spline with respect to the ! u-variable, i.e. the position of the interior knots tu(5),...,tu(nu-4) as well as the ! position of the additional knots tu(1)=...=tu(4)=0 and tu(nu-3)=...=tu(nu)=1 needed for ! the b-spline representation ! if the computation mode iopt(1)=1 is used,the values of tu(1),...,tu(nu) should be left ! unchanged between subsequent calls. if the computation mode iopt(1)=-1 is used,the values ! tu(5),...tu(nu-4) must be supplied by the user, before entry. see also the restrictions. ! nv : integer. ! unless ier=10 (in case iopt(1)>=0), nv will contain the total number of knots with respect ! to the v-variable, of the spline approximation returned. if the computation mode iopt(1)=1 ! is used, the value of nv should be left unchanged between subsequent calls. in case ! iopt(1)=-1, the value of nv should be specified on entry. ! tv : real array of dimension at least nvest. ! on successful exit, this array will contain the knots of the spline with respect to the ! v-variable, i.e. the position of the interior knots tv(5),...,tv(nv-4) as well as the ! position of the additional knots tv(1),...,tv(4) and tv(nv-3),...,tv(nv) needed for the ! b-spline representation. ! if the computation mode iopt(1)=1 is used, the values of tv(1),...,tv(nv) should be left ! unchanged between subsequent calls. if the computation mode iopt(1)=-1 is used,the values ! tv(5),...tv(nv-4) must be supplied by the user, before entry. see also the restrictions. ! u : real array of dimension at least (m). ! v : real array of dimension at least (m). ! on successful exit, u(i),v(i) contains the co-ordinates of the i-th data point with ! respect to the transformed rectangular approximation domain, for i=1,2,...,m. ! if the computation mode iopt(1)=1 is used the values of u(i),v(i) should be left unchanged ! between subsequent calls. ! c : real array of dimension at least (nuest-4)*(nvest-4). on successful exit, c contains the ! coefficients of the spline approximation s(u,v). ! fp : real. unless ier=10, fp contains the weighted sum of squared residuals of the spline ! approximation returned. ! wrk1 : real array of dimension (lwrk1). used as workspace. ! if the computation mode iopt(1)=1 is used the value of wrk1(1) should be left unchanged ! between subsequent calls. on exit wrk1(2),wrk1(3),...,wrk1(1+ncof) will contain the ! values d(i)/max(d(i)),i=1,...,ncof=1+iopt(2)*(iopt(2)+3)/2+(nv-7)*(nu-5-iopt(2)-iopt(3)) ! with d(i) the i-th diagonal element of the triangular matrix for calculating the b-spline ! coefficients.it includes those elements whose square is < eps which are treated as 0 in ! the case of rank deficiency(ier=-2). ! lwrk1 : integer. on entry lwrk1 must specify the actual dimension of the array wrk1 as declared in ! the calling (sub)program. lwrk1 must not be too small. let ! k = nuest-7, l = nvest-7, p = 1+iopt(2)*(iopt(2)+3)/2, ! q = k+2-iopt(2)-iopt(3) then ! lwrk1 >= 129+10*k+21*l+k*l+(p+l*q)*(1+8*l+p)+8*m ! wrk2 : real array of dimension (lwrk2). used as workspace, but only in the case a rank deficient ! system is encountered. ! lwrk2 : integer. on entry lwrk2 must specify the actual dimension of the array wrk2 as declared ! in the calling (sub)program. ! lwrk2 > 0 . a save upper bound for lwrk2 = (p+l*q+1)*(4*l+p)+p+l*q where p,l,q are as ! above. if there are enough data points, scattered uniformly over the approximation domain ! and if the smoothing factor s is not too small, there is a good chance that this extra ! workspace is not needed. a lot of memory might therefore be saved by setting lwrk2=1. ! iwrk : integer array of dimension (kwrk). used as workspace. ! kwrk : integer. on entry kwrk must specify the actual dimension of the array iwrk as declared in ! the calling (sub)program. kwrk >= m+(nuest-7)*(nvest-7). ! ier : integer. unless the routine detects an error, ier<=0 on exit, i.e. ! ier=0 : normal return. the spline returned has a residual sum of squares fp such that ! abs(fp-s)/s <= tol with tol a relative tolerance set to 0.001 by the program. ! ier=-1 : normal return. the spline returned is an interpolating spline (fp=0). ! ier=-2 : normal return. the spline returned is the weighted least-squares constrained polynomial. ! in this extreme case fp gives the upper bound for the smoothing factor s. ! ier<-2 : warning. the coefficients of the spline returned have been computed as the minimal norm ! least-squares solution of a (numerically) rank deficient system. (-ier) gives the rank. ! especially if the rank deficiency which can be computed as ! 1+iopt(2)*(iopt(2)+3)/2+(nv-7)*(nu-5-iopt(2)-iopt(3))+ier ! is large the results may be inaccurate. they could also depend on the value of eps. ! ier=1 : error. the required storage space exceeds the available storage space, as specified by ! the parameters nuest and nvest. likely causes: nuest or nvest too small. if these ! parameters are already large, it may also indicate that s is too small. ! the approximation returned is the weighted least-squares polar spline according to the ! current set of knots. the parameter fp gives the corresponding weighted sum of squared ! residuals (fp>s). ! ier=2 : error. a theoretically impossible result was found during the iteration process for ! finding a smoothing spline with fp = s. probably causes : s too small or badly chosen ! eps. there is an approximation returned but the corresponding weighted sum of squared ! residuals does not satisfy the condition abs(fp-s)/s < tol. ! ier=3 : error. the maximal number of iterations maxit (set to 20 by the program) allowed for ! finding a smoothing spline with fp=s has been reached. probably causes : s too small ! there is an approximation returned but the corresponding weighted sum of squared ! residuals does not satisfy the condition abs(fp-s)/s < tol. ! ier=4 : error. no more knots can be added because the dimension of the spline ! 1+iopt(2)*(iopt(2)+3)/2+(nv-7)*(nu-5-iopt(2)-iopt(3)) already exceeds the number of ! data points m. likely causes : either s or m too small. ! the approximation returned is the weighted least-squares polar spline according to the ! current set of knots. the parameter fp gives the corresponding weighted sum of squared ! residuals (fp>s). ! ier=5 : error. no more knots can be added because the additional knot would (quasi) coincide ! with an old one. likely causes : s too small or too large a weight to an inaccurate data ! point. ! the approximation returned is the weighted least-squares polar spline according to the ! current set of knots. the parameter fp gives the corresponding weighted sum of squared ! residuals (fp>s). ! ier=10 : error. on entry, the input data are controlled on validity the following restrictions ! must be satisfied: ! -1<=iopt(1)<=1 , 0<=iopt(2)<=2 , 0<=iopt(3)<=1 , ! m>=4-iopt(2)-iopt(3) , nuest>=8 ,nvest >=8, 0<eps<1, ! 0<=teta(i)<=pi, 0<=phi(i)<=2*pi, w(i)>0, i=1,...,m ! lwrk1 >= 129+10*k+21*l+k*l+(p+l*q)*(1+8*l+p)+8*m ! kwrk >= m+(nuest-7)*(nvest-7) ! if iopt(1)=-1:9<=nu<=nuest,9+iopt(2)*(iopt(2)+1)<=nv<=nvest ! 0<tu(5)<tu(6)<...<tu(nu-4)<1 ! -pi<tv(5)<tv(6)<...<tv(nv-4)<pi ! if iopt(1)>=0: s>=0 ! if one of these conditions is found to be violated,control is immediately repassed to ! the calling program. in that case there is no approximation returned. ! ier>10 : error. lwrk2 is too small, i.e. there is not enough workspace for computing the minimal ! least-squares solution of a rank deficient system of linear equations. ier gives the ! requested value for lwrk2. there is no approximation returned but, having saved the ! information contained in nu,nv,tu,tv,wrk1,u,v and having adjusted the value of lwrk2 ! and the dimension of the array wrk2 accordingly, the user can continue at the point the ! program was left, by calling polar with iopt(1)=1. ! ! further comments: ! by means of the parameter s, the user can control the tradeoff between closeness of fit and ! smoothness of fit of the approximation. if s is too large, the spline will be too smooth and ! signal will be lost ; if s is too small the spline will pick up too much noise. in the extreme ! cases the program will return an interpolating spline if s=0 and the constrained weighted least- ! squares polynomial if s is very large. between these extremes, a properly chosen s will result ! in a good compromise between closeness of fit and smoothness of fit. to decide whether an appro- ! ximation, corresponding to a certain s is satisfactory the user is highly recommended to inspect ! the fits graphically. ! recommended values for s depend on the weights w(i). if these are taken as 1/d(i) with d(i) an ! estimate of the standard deviation of z(i), a good s-value should be found in the range ! (m-sqrt(2*m),m+sqrt(2*m)). if nothing is known about the statistical error in z(i) each w(i) can ! be set equal to one and s determined by trial and error, taking account of the comments above. ! the best is then to start with a very large value of s ( to determine the least-squares poly- ! nomial and the corresponding upper bound fp0 for s) and then to progressively decrease the value ! of s ( say by a factor 10 in the beginning, i.e. s=fp0/10, fp0/100,...and more carefully as the ! approximation shows more detail) to obtain closer fits. ! to choose s very small is strongly discouraged. this considerably increases computation time and ! memory requirements. it may also cause rank-deficiency (ier<-2) and endager numerical stability. ! to economize the search for a good s-value the program provides with different modes of compu- ! tation. at the first call of the routine, or whenever he wants to restart with the initial set of ! knots the user must set iopt(1)=0. ! if iopt(1)=1 the program will continue with the set of knots found at the last call of the ! routine. this will save a lot of computation time if polar is called repeatedly for different ! values of s. ! the number of knots of the spline returned and their location will depend on the value of s and ! on the complexity of the shape of the function underlying the data. if the computation mode ! iopt(1)=1 is used, the knots returned may also depend on the s-values at previous calls (if these ! were smaller). therefore, if after a number of trials with different s-values and iopt(1)=1,the ! user can finally accept a fit as satisfactory, it may be worthwhile for him to call polar once ! more with the selected value for s but now with iopt(1)=0 indeed, polar may then return an appro- ! ximation of the same quality of fit but with fewer knots and therefore better if data reduction ! is also an important objective for the user. ! the number of knots may also depend on the upper bounds nuest and nvest. indeed, if at a certain ! stage in polar the number of knots in one direction (say nu) has reached the value of its upper ! bound (nuest), then from that moment on all subsequent knots are added in the other (v) ! direction. this may indicate that the value of nuest is too small. on the other hand, it gives ! the user the option of limiting the number of knots the routine locates in any direction ! ! other subroutines required: ! fpback,fpbspl,fppola,fpdisc,fpgivs,fprank,fprati,fprota,fporde,fprppo ! ! references: ! dierckx p.: an algorithm for fitting data over a circle using tensor ! product splines,j.comp.appl.maths 15 (1986) 161-173. ! dierckx p.: an algorithm for fitting data on a circle using tensor ! product splines, report tw68, dept. computer science, ! k.u.leuven, 1984. ! dierckx p.: curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author: ! p.dierckx ! dept. computer science, k.u. leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! creation date : june 1984 ! latest update : march 1989 ! ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: s,eps real(RKIND), intent(inout) :: fp integer, intent(in) :: m,nuest,nvest,lwrk1,lwrk2,kwrk integer, intent(out) :: nu,nv,ier ! ..array arguments.. real(RKIND), intent(in) :: x(m),y(m),z(m),w(m) real(RKIND), intent(out) :: u(m),v(m),tu(nuest),tv(nvest),c((nuest-4)*(nvest-4)) real(RKIND), intent(inout) :: wrk1(lwrk1),wrk2(lwrk2) integer, intent(in) :: iopt(3) integer, intent(inout) :: iwrk(kwrk) ! ..user specified function procedure(boundary) :: rad ! ..local scalars.. real(RKIND) :: dist,r integer :: i,ib1,ib3,ki,kn,kwest,la,lbu,lcc,lcs,lro,lbv,lco,lf,lff,lfp,lh,lq,lsu,lsv,lwest,& ncest,ncc,nuu,nvv,nreg,nrint,nu4,nv4,iopt1,iopt2,iopt3,ipar,nvmin ! set up constants integer , parameter :: maxit = 20 real(RKIND), parameter :: tol = smallnum03 ! before starting computations a data check is made. if the input data ! are invalid,control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR iopt1 = iopt(1) iopt2 = iopt(2) iopt3 = iopt(3) if (eps<=zero .or. eps>=one) return if (iopt1<(-1) .or. iopt1>1) return if (iopt2<0 .or. iopt2>2) return if (iopt3<0 .or. iopt3>1) return if (m<(4-iopt2-iopt3)) return if (nuest<8 .or. nvest<8) return nu4 = nuest-4 nv4 = nvest-4 ncest = nu4*nv4 nuu = nuest-7 nvv = nvest-7 ipar = 1+iopt2*(iopt2+3)/2 ncc = ipar+nvv*(nuest-5-iopt2-iopt3) nrint = nuu+nvv nreg = nuu*nvv ib1 = 4*nvv ib3 = ib1+ipar lwest = ncc*(1+ib1+ib3)+2*nrint+ncest+m*8+ib3+5*nuest+12*nvest kwest = m+nreg if (lwrk1<lwest .or. kwrk<kwest) return if (iopt1<=0) then ! Check weights if (any(w<=zero)) return ! Get polar coordinates do i=1,m dist = x(i)**2+y(i)**2 if (dist<=zero) then u(i) = zero v(i) = zero else v(i) = atan2(y(i),x(i)) r = rad(v(i)); if (r<=zero) return u(i) = sqrt(dist)/r; if (u(i)>one) return endif end do endif if (iopt1<0) then nuu = nu-8 nvv = nv-8 nvmin = 9+iopt2*(iopt2+1) if (nuu<1 .or. nu>nuest) return if (nv<nvmin .or. nv>nvest) return tu(4) = zero if (any(tu(5:nuu+4)<=tu(4:nuu+3) .or. tu(5:nuu+4)>=one)) return tv(4) = -pi if (any(tv(5:nvv+4)<=tv(4:nvv+3) .or. tv(5:nvv+4)>=pi)) return endif if (iopt1>=0 .and. s<zero) return ier = FITPACK_OK ! we partition the working space and determine the spline approximation kn = 1 ki = kn+m lq = 2 la = lq+ncc*ib3 lf = la+ncc*ib1 lff = lf+ncc lfp = lff+ncest lco = lfp+nrint lh = lco+nrint lbu = lh+ib3 lbv = lbu+5*nuest lro = lbv+5*nvest lcc = lro+nvest lcs = lcc+nvest lsu = lcs+nvest*5 lsv = lsu+m*4 call fppola(iopt1,iopt2,iopt3,m,u,v,z,w,rad,s,nuest,nvest,eps,tol, & maxit,ib1,ib3,ncest,ncc,nrint,nreg,nu,tu,nv,tv,c,fp,wrk1(1), & wrk1(lfp),wrk1(lco),wrk1(lf),wrk1(lff),wrk1(lro),wrk1(lcc), & wrk1(lcs),wrk1(la),wrk1(lq),wrk1(lbu),wrk1(lbv),wrk1(lsu), & wrk1(lsv),wrk1(lh),iwrk(ki),iwrk(kn),wrk2,lwrk2,ier) return end subroutine polar ! subroutine profil calculates the b-spline coefficients of the univariate spline ! if iopt=0, f(y) = s(u,y) ! if iopt=1, g(x) = s(x,u) ! with s(x,y) a bivariate spline of degrees kx and ky, given in the b-spline representation. pure subroutine profil(iopt,tx,nx,ty,ny,c,kx,ky,u,nu,cu,ier) ! ! calling sequence: ! call profil(iopt,tx,nx,ty,ny,c,kx,ky,u,nu,cu,ier) ! ! input parameters: ! iopt : integer flag, specifying whether the profile f(y) (iopt=0) or the profile g(x) (iopt=1) ! must be determined. ! tx : real array, length nx, which contains the position of the knots in the x-direction. ! nx : integer, giving the total number of knots in the x-direction ! ty : real array, length ny, which contains the position of the knots in the y-direction. ! ny : integer, giving the total number of knots in the y-direction ! c : real array, length (nx-kx-1)*(ny-ky-1), which contains the b-spline coefficients. ! kx,ky : integer values, giving the degrees of the spline. ! u : real value, specifying the requested profile. ! tx(kx+1)<=u<=tx(nx-kx), if iopt=0. ! ty(ky+1)<=u<=ty(ny-ky), if iopt=1. ! nu : on entry nu must specify the dimension of the array cu. ! nu >= ny if iopt=0, nu >= nx if iopt=1. ! ! output parameters: ! cu : real array of dimension (nu). on successful exit this array contains the b-spline ! ier : integer error flag ! ! restrictions: ! if iopt=0 : tx(kx+1) <= u <= tx(nx-kx), nu >=ny. ! if iopt=1 : ty(ky+1) <= u <= ty(ny-ky), nu >=nx. ! ! other subroutines required: ! fpbspl ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! ..scalar arguments.. integer, intent(in) :: iopt,nx,ny,kx,ky,nu integer, intent(out) :: ier real(RKIND), intent(in) :: u ! ..array arguments.. real(RKIND), intent(in) :: tx(nx),ty(ny),c((nx-kx-1)*(ny-ky-1)) real(RKIND), intent(out) :: cu(nu) ! ..local scalars.. integer :: i,kx1,ky1,l,l1,m0,nkx1,nky1 ! ..local array real(RKIND) :: h(MAX_ORDER+1) ! .. ! before starting computations a data check is made. if the input data ! are invalid control is immediately repassed to the calling program. kx1 = kx+1 ky1 = ky+1 nkx1 = nx-kx1 nky1 = ny-ky1 ier = FITPACK_INPUT_ERROR select case (iopt) case (0) if (nu<ny) return if (u<tx(kx1) .or. u>tx(nkx1+1)) return ! the b-splinecoefficients of f(y) = s(u,y). ier = FITPACK_OK l = kx1 l1 = l+1 do while (u>=tx(l1) .and. l/=nkx1) l = l1 l1 = l+1 end do h = fpbspl(tx,nx,kx,u,l) m0 = (l-kx1)*nky1+1 do i=1,nky1 cu(i) = dot_product(h(1:kx1),c(m0:m0+nky1*kx:nky1)) m0 = m0+1 end do case (1) if (nu<nx) return if (u<ty(ky1) .or. u>ty(nky1+1)) return ! the b-splinecoefficients of g(x) = s(x,u). ier = FITPACK_OK l = ky1 l1 = l+1 do while (u>=ty(l1) .and. l/=nky1) l = l1 l1 = l+1 end do h = fpbspl(ty,ny,ky,u,l) m0 = l-ky do i=1,nkx1 cu(i) = dot_product(h(1:ky1),c(m0:m0+ky)) m0 = m0+nky1 end do end select return end subroutine profil ! given the set of values z(i,j) on the rectangular grid (x(i),y(j)),i=1,...,mx;j=1,...,my, subroutine ! regrid determines a smooth bivariate spline approximation s(x,y) of degrees kx and ky on the rect- ! angle xb <= x <= xe, yb <= y <= ye. ! if iopt = -1 regrid calculates the least-squares spline according to a given set of knots. ! if iopt >= 0 the total numbers nx and ny of these knots and their position tx(j),j=1,...,nx and ! ty(j),j=1,...,ny are chosen automatically by the routine. the smoothness of s(x,y) is then achieved by ! minimalizing the discontinuity jumps in the derivatives of s(x,y) across the boundaries of the ! subpanels (tx(i),tx(i+1))*(ty(j),ty(j+1). the amounth of smoothness is determined by the condition ! that f(p) = sum ((z(i,j)-s(x(i),y(j))))**2) be <= s, with s a given non-negative constant, called the ! smoothing factor. the fit is given in the b-spline representation (b-spline coefficients ! c((ny-ky-1)*(i-1)+j),i=1,...,nx-kx-1;j=1,...,ny-ky-1) and can be evaluated by means of subroutine ! bispev. pure subroutine regrid(iopt,mx,x,my,y,z,xb,xe,yb,ye,kx,ky,s, & nxest,nyest,nx,tx,ny,ty,c,fp,wrk,lwrk,iwrk,kwrk,ier) ! ! calling sequence: ! call regrid(iopt,mx,x,my,y,z,xb,xe,yb,ye,kx,ky,s,nxest,nyest, ! * nx,tx,ny,ty,c,fp,wrk,lwrk,iwrk,kwrk,ier) ! ! parameters: ! iopt : integer flag. on entry iopt must specify whether a least-squares spline (iopt=-1) or a ! smoothing spline (iopt=0 or 1) must be determined. ! if iopt=0 the routine will start with an initial set of knots ! tx(i)=xb,tx(i+kx+1)=xe,i=1,...,kx+1;ty(i)=yb,ty(i+ky+1)=ye,i=1,...,ky+1. ! if iopt=1 the routine will continue with the set of knots found at the last call of the ! routine. attention: a call with iopt=1 must always be immediately preceded by another call ! with iopt=1 or iopt=0 and s/=0. unchanged on exit. ! mx : integer. on entry mx must specify the number of grid points along the x-axis. mx > kx . ! unchanged on exit. ! x : real array of dimension at least (mx). before entry, x(i) must be set to the x-co-ordinate of ! the i-th grid point along the x-axis, for i=1,2,...,mx. these values must be supplied in ! strictly ascending order. unchanged on exit. ! my : integer. on entry my must specify the number of grid points along the y-axis. my > ky . ! unchanged on exit. ! y : real array of dimension at least (my). before entry, y(j) must be set to the y-co-ordinate of ! the j-th grid point along the y-axis, for j=1,2,...,my. these values must be supplied in ! strictly ascending order. unchanged on exit. ! z : real array of dimension at least (mx*my). before entry, z(my*(i-1)+j) must be set to the data ! value at the grid point (x(i),y(j)) for i=1,...,mx and j=1,...,my. unchanged on exit. ! xb,xe : real values. on entry xb,xe,yb and ye must specify the boundaries of the rectangular approxi- ! yb,ye mation domain. xb<=x(i)<=xe,i=1,...,mx; yb<=y(j)<=ye,j=1,...,my. unchanged on exit. ! kx,ky : integer values. on entry kx and ky must specify the degrees of the spline. 1<=kx,ky<=5. it is ! recommended to use bicubic (kx=ky=3) splines. unchanged on exit. ! s : real. on entry (in case iopt>=0) s must specify the smoothing factor. s>=0. unchanged on exit. ! for advice on the choice of s see further comments ! nxest : integer. unchanged on exit. ! nyest : integer. unchanged on exit. ! on entry, nxest and nyest must specify an upper bound for the number of knots required in the ! x- and y-directions respect. these numbers will also determine the storage space needed by the ! routine. nxest >= 2*(kx+1), nyest >= 2*(ky+1). in most practical situation nxest = mx/2, ! nyest=my/2, will be sufficient. always large enough are nxest=mx+kx+1, nyest=my+ky+1, the ! number of knots needed for interpolation (s=0). see also further comments. ! nx : integer. ! unless ier=10 (in case iopt >=0), nx will contain the total number of knots with respect to ! the x-variable, of the spline approximation returned. if the computation mode iopt=1 is used, ! the value of nx should be left unchanged between subsequent calls. ! in case iopt=-1, the value of nx should be specified on entry ! tx : real array of dimension nmax. on successful exit, this array will contain the knots of the ! spline with respect to the x-variable, i.e. the position of the interior knots tx(kx+2),..., ! tx(nx-kx-1) as well as the position of the additional knots tx(1)=...=tx(kx+1)=xb and ! tx(nx-kx)=...=tx(nx)=xe needed for the b-spline representat. if the computation mode iopt=1 ! is used, the values of tx(1),...,tx(nx) should be left unchanged between subsequent calls. ! if the computation mode iopt=-1 is used, the values tx(kx+2),...tx(nx-kx-1) must be supplied ! by the user, before entry. see also the restrictions (ier=10). ! ny : integer. ! unless ier=10 (in case iopt >=0), ny will contain the total number of knots with respect to ! the y-variable, of the spline approximation returned. if the computation mode iopt=1 is used, ! the value of ny should be left unchanged between subsequent calls. ! in case iopt=-1, the value of ny should be specified on entry ! ty : real array of dimension nmax. ! on successful exit, this array will contain the knots of the spline with respect to the ! y-variable, i.e. the position of the interior knots ty(ky+2),...,ty(ny-ky-1) as well as the ! position of the additional knots ty(1)=...=ty(ky+1)=yb and ty(ny-ky)=...=ty(ny)=ye needed for ! the b-spline representat. if the computation mode iopt=1 is used, the values of ty(1),..., ! ty(ny) should be left unchanged between subsequent calls. ! if the computation mode iopt=-1 is used, the values ty(ky+2),...ty(ny-ky-1) must be supplied ! by the user, before entry. see also the restrictions (ier=10). ! c : real array of dimension at least (nxest-kx-1)*(nyest-ky-1). ! on successful exit, c contains the coefficients of the spline approximation s(x,y) ! fp : real. unless ier=10, fp contains the sum of squared residuals of the spline approximation ! returned. ! wrk : real array of dimension (lwrk). used as workspace. if the computation mode iopt=1 is used the ! values of wrk(1),...,wrk(4) should be left unchanged between subsequent calls. ! lwrk : integer. on entry lwrk must specify the actual dimension of the array wrk as declared in the ! calling (sub)program. lwrk must not be too small. ! lwrk >= 4+nxest*(my+2*kx+5)+nyest*(2*ky+5)+mx*(kx+1)+ my*(ky+1) +u ! where u is the larger of my and nxest. ! iwrk : integer array of dimension (kwrk). used as workspace. if the computation mode iopt=1 is used ! the values of iwrk(1),...,iwrk(3) should be left unchanged between subsequent calls ! kwrk : integer. on entry kwrk must specify the actual dimension of the array iwrk as declared in the ! calling (sub)program. kwrk >= 3+mx+my+nxest+nyest. ! ier : integer. unless the routine detects an error, ier contains a non-positive value on exit, i.e. ! ier=0 : normal return. the spline returned has a residual sum of squares fp such that abs(fp-s)/s ! <= tol with tol a relative tolerance set to 0.001 by the program. ! ier=-1 : normal return. the spline returned is an interpolating spline (fp=0). ! ier=-2 : normal return. the spline returned is the least-squares polynomial of degrees kx and ky. in ! this extreme case fp gives the upper bound for the smoothing factor s. ! ier=1 : error. the required storage space exceeds the available storage space, as specified by the ! parameters nxest and nyest. ! probably causes : nxest or nyest too small. if these parameters are already large, it may ! also indicate that s is too small. the approximation returned is the least-squares spline ! according to the current set of knots. the parameter fp gives the corresponding sum of ! squared residuals (fp>s). ! ier=2 : error. a theoretically impossible result was found during the iteration process for finding ! a smoothing spline with fp = s. probably causes : s too small. there is an approximation ! returned but the corresponding sum of squared residuals does not satisfy the condition ! abs(fp-s)/s < tol. ! ier=3 : error. the maximal number of iterations maxit (set to 20 by the program) allowed for finding ! a smoothing spline with fp=s has been reached. probably causes : s too small. there is an ! approximation returned but the corresponding sum of squared residuals does not satisfy the ! condition abs(fp-s)/s < tol. ! ier=10 : error. on entry, the input data are controlled on validity the following restrictions must ! be satisfied. ! -1<=iopt<=1, 1<=kx,ky<=5, mx>kx, my>ky, nxest>=2*kx+2, ! nyest>=2*ky+2, kwrk>=3+mx+my+nxest+nyest, ! lwrk >= 4+nxest*(my+2*kx+5)+nyest*(2*ky+5)+mx*(kx+1)+ ! my*(ky+1) +max(my,nxest), ! xb<=x(i-1)<x(i)<=xe,i=2,..,mx,yb<=y(j-1)<y(j)<=ye,j=2,..,my ! if iopt=-1: 2*kx+2<=nx<=min(nxest,mx+kx+1) ! xb<tx(kx+2)<tx(kx+3)<...<tx(nx-kx-1)<xe ! 2*ky+2<=ny<=min(nyest,my+ky+1) ! yb<ty(ky+2)<ty(ky+3)<...<ty(ny-ky-1)<ye ! the schoenberg-whitney conditions, i.e. there must ! be subset of grid co-ordinates xx(p) and yy(q) such ! that tx(p) < xx(p) < tx(p+kx+1) ,p=1,...,nx-kx-1 ! ty(q) < yy(q) < ty(q+ky+1) ,q=1,...,ny-ky-1 ! if iopt>=0: s>=0 ! if s=0 : nxest>=mx+kx+1, nyest>=my+ky+1 ! if one of these conditions is found to be violated,control ! is immediately repassed to the calling program. in that ! case there is no approximation returned. ! ! further comments: ! regrid does not allow individual weighting of the data-values. so, if these were determined to widely ! different accuracies, then perhaps the general data set routine surfit should rather be used in spite ! of efficiency. by means of the parameter s, the user can control the tradeoff between closeness of ! fit and smoothness of fit of the approximation. if s is too large, the spline will be too smooth and ! signal will be lost ; if s is too small the spline will pick up too much noise. in the extreme cases ! the program will return an interpolating spline if s=0 and the least-squares polynomial (degrees ! kx,ky) if s is very large. between these extremes, a properly chosen s will result in a good ! compromise between closeness of fit and smoothness of fit. to decide whether an approximation, cor- ! responding to a certain s is satisfactory the user is highly recommended to inspect the fits ! graphically. ! recommended values for s depend on the accuracy of the data values. if the user has an idea of the ! statistical errors on the data, he can also find a proper estimate for s. for, by assuming that, if ! he specifies the right s, regrid will return a spline s(x,y) which exactly reproduces the function ! underlying the data he can evaluate the sum((z(i,j)-s(x(i),y(j)))**2) to find a good estimate for ! this s. for example, if he knows that the statistical errors on his z(i,j)- values is not greater ! than 0.1, he may expect that a good s should have a value not larger than mx*my*(0.1)**2. ! if nothing is known about the statistical error in z(i,j), s must be determined by trial and error, ! taking account of the comments above. the best is then to start with a very large value of s (to ! determine the least-squares polynomial and the corresponding upper bound fp0 for s) and then to ! progressively decrease the value of s (say by a factor 10 in the beginning, i.e. s=fp0/10,fp0/100,... ! and more carefully as the approximation shows more detail) to obtain closer fits. ! to economize the search for a good s-value the program provides with different modes of computation. ! at the first call of the routine, or whenever he wants to restart with the initial set of knots the ! user must set iopt=0. ! if iopt=1 the program will continue with the set of knots found at the last call of the routine. this ! will save a lot of computation time if regrid is called repeatedly for different values of s. the ! number of knots of the spline returned and their location will depend on the value of s and on the ! complexity of the shape of the function underlying the data. if the computation mode iopt=1 is used, ! the knots returned may also depend on the s-values at previous calls (if these were smaller). ! therefore, if after a number of trials with different s-values and iopt=1, the user can finally ! accept a fit as satisfactory, it may be worthwhile for him to call regrid once more with the selected ! value for s but now with iopt=0. indeed, regrid may then return an approximation of the same quality ! of fit but with fewer knots and therefore better if data reduction is also an important objective for ! the user. the number of knots may also depend on the upper bounds nxest and nyest. indeed, if at a ! certain stage in regrid the number of knots in one direction (say nx) has reached the value of its ! upper bound (nxest), then from that moment on all subsequent knots are added in the other (y) ! direction. this may indicate that the value of nxest is too small. on the other hand, it gives the ! user the option of limiting the number of knots the routine locates in any direction. for example, ! by setting nxest=2*kx+2 (the lowest allowable value for nxest), the user can indicate that he wants ! an approximation which is a simple polynomial of degree kx in the variable x. ! ! other subroutines required: ! fpback,fpbspl,fpregr,fpdisc,fpgivs,fpgrre,fprati,fprota,fpchec,fpknot ! ! references: ! dierckx p. : a fast algorithm for smoothing data on a rectangular ! grid while using spline functions, siam j.numer.anal. ! 19 (1982) 1286-1304. ! dierckx p. : a fast algorithm for smoothing data on a rectangular ! grid while using spline functions, report tw53, dept. ! computer science,k.u.leuven, 1980. ! dierckx p. : curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author: ! p.dierckx ! dept. computer science, k.u. leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! creation date : may 1979 ! ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: xb,xe,yb,ye,s real(RKIND), intent(out) :: fp integer, intent(in) :: iopt,mx,my,kx,ky,nxest,nyest,lwrk,kwrk integer, intent(inout) :: nx,ny integer, intent(out) :: ier ! ..array arguments.. real(RKIND), intent(in) :: x(mx),y(my),z(mx*my) real(RKIND), intent(inout) :: tx(nxest),ty(nyest),c((nxest-kx-1)*(nyest-ky-1)),wrk(lwrk) integer, intent(inout) :: iwrk(kwrk) ! ..local scalars.. integer :: jwrk,kndx,kndy,knrx,knry,kwest,kx1,kx2,ky1,ky2,lfpx,lfpy,lwest,lww,nc,nminx,nminy,mz ! ..subroutine references.. ! fpregr,fpchec ! .. ! we set up the parameters tol and maxit. integer, parameter :: maxit = 20 real(RKIND), parameter :: tol = smallnum03 ! before starting computations a data check is made. if the input data ! are invalid, control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR kx1 = kx+1 kx2 = kx1+1 ky1 = ky+1 ky2 = ky1+1 nminx = 2*kx1 nminy = 2*ky1 mz = mx*my nc = (nxest-kx1)*(nyest-ky1) if (kx<=0 .or. kx>5) return if (ky<=0 .or. ky>5) return if (iopt<(-1) .or. iopt>1) return if (mx<kx1 .or. nxest<nminx) return if (my<ky1 .or. nyest<nminy) return lwest = 4+nxest*(my+2*kx2+1)+nyest*(2*ky2+1)+mx*kx1+my*ky1+max(nxest,my) kwest = 3+mx+my+nxest+nyest if (lwrk<lwest .or. kwrk<kwest) return if (xb>x(1) .or. xe<x(mx)) return if (yb>y(1) .or. ye<y(my)) return if (any(x(1:mx-1)>=x(2:mx))) return if (any(y(1:my-1)>=y(2:my))) return if (iopt<0) then if (nx<nminx .or. nx>nxest) return tx(1:kx1) = xb tx(nx-kx:nx) = xe ier = fpchec(x,mx,tx,nx,kx); if (ier/=FITPACK_OK) return if (ny<nminy .or. ny>nyest) return ty(1:ky1) = yb ty(ny-ky:ny) = ye ier = fpchec(y,my,ty,ny,ky); if (ier/=FITPACK_OK) return else if (s<zero) return if (equal(s,zero) .and. (nxest<(mx+kx1) .or. nyest<(my+ky1)) ) return endif ! we partition the working space and determine the spline approximation ier = FITPACK_OK lfpx = 5 lfpy = lfpx+nxest lww = lfpy+nyest jwrk = lwrk-4-nxest-nyest knrx = 4 knry = knrx+mx kndx = knry+my kndy = kndx+nxest call fpregr(iopt,x,mx,y,my,z,mz,xb,xe,yb,ye,kx,ky,s,nxest,nyest, & tol,maxit,nc,nx,tx,ny,ty,c,fp,wrk(1),wrk(2),wrk(3),wrk(4), & wrk(lfpx),wrk(lfpy),iwrk(1),iwrk(2),iwrk(3),iwrk(knrx), & iwrk(knry),iwrk(kndx),iwrk(kndy),wrk(lww),jwrk,ier) return end subroutine regrid ! subroutine spalde evaluates at a point x ALL the derivatives ! (j-1) ! d(j) = s (x) , j=1,2,...,k1 ! of a spline s(x) of order k1 (degree k=k1-1), given in its b-spline representation. pure subroutine spalde(t,n,c,k1,x,d,ier) ! calling sequence: ! call spalde(t,n,c,k1,x,d,ier) ! ! input parameters: ! t : array,length n, which contains the position of the knots. ! n : integer, giving the total number of knots of s(x). ! c : array,length n, which contains the b-spline coefficients. ! k1 : integer, giving the order of s(x) (order=degree+1) ! x : real, which contains the point where the derivatives must ! be evaluated. ! ! output parameters: ! d : array,length k1, containing the derivative values of s(x). ! ier : error flag ! ier = 0 : normal return ! ier =10 : invalid input data (see restrictions) ! ! restrictions: ! t(k1) <= x <= t(n-k1+1) ! ! further comments: ! if x coincides with a knot, right derivatives are computed ! ( left derivatives if x = t(n-k1+1) ). ! ! other subroutines required: fpader. ! ! references : ! de boor c : on calculating with b-splines, j. approximation theory ! 6 (1972) 50-62. ! cox m.g. : the numerical evaluation of b-splines, j. inst. maths ! applics 10 (1972) 134-149. ! dierckx p. : curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! latest update : march 1987 ! ! ..scalar arguments.. integer, intent(in) :: n,k1 integer, intent(out) :: ier real(RKIND), intent(in) :: x ! ..array arguments.. real(RKIND), intent(in) :: t(n),c(n) real(RKIND), intent(out) :: d(k1) ! ..local scalars.. integer :: l,nk1 ! .. ! before starting computations a data check is made. if the input data ! are invalid control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR nk1 = n-k1 if(x<t(k1) .or. x>t(nk1+1)) return ! search for knot interval t(l) <= x < t(l+1) l = k1 do while (.not.(x<t(l+1) .or. l==nk1)) l = l+1 end do if(t(l)>=t(l+1)) return ! calculate the derivatives. ier = FITPACK_OK call fpader(t,n,c,k1,x,l,d) end subroutine spalde pure subroutine spgrid(iopt,ider,mu,u,mv,v,r,r0,r1,s, & nuest,nvest,nu,tu,nv,tv,c,fp,wrk,lwrk,iwrk,kwrk,ier) ! given the function values r(i,j) on the latitude-longitude grid ! (u(i),v(j)), i=1,...,mu ; j=1,...,mv , spgrid determines a smooth ! bicubic spline approximation on the rectangular domain 0<=u<=pi, ! vb<=v<=ve (vb = v(1), ve=vb+2*pi). ! this approximation s(u,v) will satisfy the properties ! ! (1) s(0,v) = s(0,0) = dr(1) ! ! d s(0,v) d s(0,0) d s(0,pi/2) ! (2) -------- = cos(v)* -------- + sin(v)* ----------- ! d u d u d u ! ! = cos(v)*dr(2)+sin(v)*dr(3) ! vb <= v <= ve ! (3) s(pi,v) = s(pi,0) = dr(4) ! ! d s(pi,v) d s(pi,0) d s(pi,pi/2) ! (4) -------- = cos(v)* --------- + sin(v)* ------------ ! d u d u d u ! ! = cos(v)*dr(5)+sin(v)*dr(6) ! ! and will be periodic in the variable v, i.e. ! ! j j ! d s(u,vb) d s(u,ve) ! (5) --------- = --------- 0 <=u<= pi , j=0,1,2 ! j j ! d v d v ! ! the number of knots of s(u,v) and their position tu(i),i=1,2,...,nu; ! tv(j),j=1,2,...,nv, is chosen automatically by the routine. the ! smoothness of s(u,v) is achieved by minimalizing the discontinuity ! jumps of the derivatives of the spline at the knots. the amount of ! smoothness of s(u,v) is determined by the condition that ! fp=sumi=1,mu(sumj=1,mv((r(i,j)-s(u(i),v(j)))**2))+(r0-s(0,v))**2 ! + (r1-s(pi,v))**2 <= s, with s a given non-negative constant. ! the fit s(u,v) is given in its b-spline representation and can be ! evaluated by means of routine bispev ! ! calling sequence: ! call spgrid(iopt,ider,mu,u,mv,v,r,r0,r1,s,nuest,nvest,nu,tu, ! * ,nv,tv,c,fp,wrk,lwrk,iwrk,kwrk,ier) ! ! parameters: ! iopt : integer array of dimension 3, specifying different options. ! unchanged on exit. ! iopt(1):on entry iopt(1) must specify whether a least-squares spline ! (iopt(1)=-1) or a smoothing spline (iopt(1)=0 or 1) must be ! determined. ! if iopt(1)=0 the routine will start with an initial set of ! knots tu(i)=0,tu(i+4)=pi,i=1,...,4;tv(i)=v(1)+(i-4)*2*pi, ! i=1,...,8. ! if iopt(1)=1 the routine will continue with the set of knots ! found at the last call of the routine. ! attention: a call with iopt(1)=1 must always be immediately ! preceded by another call with iopt(1) = 1 or iopt(1) = 0. ! iopt(2):on entry iopt(2) must specify the requested order of conti- ! nuity at the pole u=0. ! if iopt(2)=0 only condition (1) must be fulfilled and ! if iopt(2)=1 conditions (1)+(2) must be fulfilled. ! iopt(3):on entry iopt(3) must specify the requested order of conti- ! nuity at the pole u=pi. ! if iopt(3)=0 only condition (3) must be fulfilled and ! if iopt(3)=1 conditions (3)+(4) must be fulfilled. ! ider : integer array of dimension 4, specifying different options. ! unchanged on exit. ! ider(1):on entry ider(1) must specify whether (ider(1)=0 or 1) or not ! (ider(1)=-1) there is a data value r0 at the pole u=0. ! if ider(1)=1, r0 will be considered to be the right function ! value, and it will be fitted exactly (s(0,v)=r0). ! if ider(1)=0, r0 will be considered to be a data value just ! like the other data values r(i,j). ! ider(2):on entry ider(2) must specify whether (ider(2)=1) or not ! (ider(2)=0) the approximation has vanishing derivatives ! dr(2) and dr(3) at the pole u=0 (in case iopt(2)=1) ! ider(3):on entry ider(3) must specify whether (ider(3)=0 or 1) or not ! (ider(3)=-1) there is a data value r1 at the pole u=pi. ! if ider(3)=1, r1 will be considered to be the right function ! value, and it will be fitted exactly (s(pi,v)=r1). ! if ider(3)=0, r1 will be considered to be a data value just ! like the other data values r(i,j). ! ider(4):on entry ider(4) must specify whether (ider(4)=1) or not ! (ider(4)=0) the approximation has vanishing derivatives ! dr(5) and dr(6) at the pole u=pi (in case iopt(3)=1) ! mu : integer. on entry mu must specify the number of grid points ! along the u-axis. unchanged on exit. ! mu >= 1, mu >=mumin=4-i0-i1-ider(2)-ider(4) with ! i0=min(1,ider(1)+1), i1=min(1,ider(3)+1) ! u : real array of dimension at least (mu). before entry, u(i) ! must be set to the u-co-ordinate of the i-th grid point ! along the u-axis, for i=1,2,...,mu. these values must be ! supplied in strictly ascending order. unchanged on exit. ! 0 < u(i) < pi. ! mv : integer. on entry mv must specify the number of grid points ! along the v-axis. mv > 3 . unchanged on exit. ! v : real array of dimension at least (mv). before entry, v(j) ! must be set to the v-co-ordinate of the j-th grid point ! along the v-axis, for j=1,2,...,mv. these values must be ! supplied in strictly ascending order. unchanged on exit. ! -pi <= v(1) < pi , v(mv) < v(1)+2*pi. ! r : real array of dimension at least (mu*mv). ! before entry, r(mv*(i-1)+j) must be set to the data value at ! the grid point (u(i),v(j)) for i=1,...,mu and j=1,...,mv. ! unchanged on exit. ! r0 : real value. on entry (if ider(1) >=0 ) r0 must specify the ! data value at the pole u=0. unchanged on exit. ! r1 : real value. on entry (if ider(1) >=0 ) r1 must specify the ! data value at the pole u=pi. unchanged on exit. ! s : real. on entry (if iopt(1)>=0) s must specify the smoothing ! factor. s >=0. unchanged on exit. ! for advice on the choice of s see further comments ! nuest : integer. unchanged on exit. ! nvest : integer. unchanged on exit. ! on entry, nuest and nvest must specify an upper bound for the ! number of knots required in the u- and v-directions respect. ! these numbers will also determine the storage space needed by ! the routine. nuest >= 8, nvest >= 8. ! in most practical situation nuest = mu/2, nvest=mv/2, will ! be sufficient. always large enough are nuest=mu+6+iopt(2)+ ! iopt(3), nvest = mv+7, the number of knots needed for ! interpolation (s=0). see also further comments. ! nu : integer. ! unless ier=10 (in case iopt(1)>=0), nu will contain the total ! number of knots with respect to the u-variable, of the spline ! approximation returned. if the computation mode iopt(1)=1 is ! used, the value of nu should be left unchanged between sub- ! sequent calls. in case iopt(1)=-1, the value of nu should be ! specified on entry. ! tu : real array of dimension at least (nuest). ! on successful exit, this array will contain the knots of the ! spline with respect to the u-variable, i.e. the position of ! the interior knots tu(5),...,tu(nu-4) as well as the position ! of the additional knots tu(1)=...=tu(4)=0 and tu(nu-3)=...= ! tu(nu)=pi needed for the b-spline representation. ! if the computation mode iopt(1)=1 is used,the values of tu(1) ! ...,tu(nu) should be left unchanged between subsequent calls. ! if the computation mode iopt(1)=-1 is used, the values tu(5), ! ...tu(nu-4) must be supplied by the user, before entry. ! see also the restrictions (ier=10). ! nv : integer. ! unless ier=10 (in case iopt(1)>=0), nv will contain the total ! number of knots with respect to the v-variable, of the spline ! approximation returned. if the computation mode iopt(1)=1 is ! used, the value of nv should be left unchanged between sub- ! sequent calls. in case iopt(1) = -1, the value of nv should ! be specified on entry. ! tv : real array of dimension at least (nvest). ! on successful exit, this array will contain the knots of the ! spline with respect to the v-variable, i.e. the position of ! the interior knots tv(5),...,tv(nv-4) as well as the position ! of the additional knots tv(1),...,tv(4) and tv(nv-3),..., ! tv(nv) needed for the b-spline representation. ! if the computation mode iopt(1)=1 is used,the values of tv(1) ! ...,tv(nv) should be left unchanged between subsequent calls. ! if the computation mode iopt(1)=-1 is used, the values tv(5), ! ...tv(nv-4) must be supplied by the user, before entry. ! see also the restrictions (ier=10). ! c : real array of dimension at least (nuest-4)*(nvest-4). ! on successful exit, c contains the coefficients of the spline ! approximation s(u,v) ! fp : real. unless ier=10, fp contains the sum of squared ! residuals of the spline approximation returned. ! wrk : real array of dimension (lwrk). used as workspace. ! if the computation mode iopt(1)=1 is used the values of ! wrk(1),..,wrk(12) should be left unchanged between subsequent ! calls. ! lwrk : integer. on entry lwrk must specify the actual dimension of ! the array wrk as declared in the calling (sub)program. ! lwrk must not be too small. ! lwrk >= 12+nuest*(mv+nvest+3)+nvest*24+4*mu+8*mv+q ! where q is the larger of (mv+nvest) and nuest. ! iwrk : integer array of dimension (kwrk). used as workspace. ! if the computation mode iopt(1)=1 is used the values of ! iwrk(1),.,iwrk(5) should be left unchanged between subsequent ! calls. ! kwrk : integer. on entry kwrk must specify the actual dimension of ! the array iwrk as declared in the calling (sub)program. ! kwrk >= 5+mu+mv+nuest+nvest. ! ier : integer. unless the routine detects an error, ier contains a ! non-positive value on exit, i.e. ! ier=0 : normal return. the spline returned has a residual sum of ! squares fp such that abs(fp-s)/s <= tol with tol a relat- ! ive tolerance set to 0.001 by the program. ! ier=-1 : normal return. the spline returned is an interpolating ! spline (fp=0). ! ier=-2 : normal return. the spline returned is the least-squares ! constrained polynomial. in this extreme case fp gives the ! upper bound for the smoothing factor s. ! ier=1 : error. the required storage space exceeds the available ! storage space, as specified by the parameters nuest and ! nvest. ! probably causes : nuest or nvest too small. if these param- ! eters are already large, it may also indicate that s is ! too small ! the approximation returned is the least-squares spline ! according to the current set of knots. the parameter fp ! gives the corresponding sum of squared residuals (fp>s). ! ier=2 : error. a theoretically impossible result was found during ! the iteration process for finding a smoothing spline with ! fp = s. probably causes : s too small. ! there is an approximation returned but the corresponding ! sum of squared residuals does not satisfy the condition ! abs(fp-s)/s < tol. ! ier=3 : error. the maximal number of iterations maxit (set to 20 ! by the program) allowed for finding a smoothing spline ! with fp=s has been reached. probably causes : s too small ! there is an approximation returned but the corresponding ! sum of squared residuals does not satisfy the condition ! abs(fp-s)/s < tol. ! ier=10 : error. on entry, the input data are controlled on validity ! the following restrictions must be satisfied. ! -1<=iopt(1)<=1, 0<=iopt(2)<=1, 0<=iopt(3)<=1, ! -1<=ider(1)<=1, 0<=ider(2)<=1, ider(2)=0 if iopt(2)=0. ! -1<=ider(3)<=1, 0<=ider(4)<=1, ider(4)=0 if iopt(3)=0. ! mu >= mumin (see above), mv >= 4, nuest >=8, nvest >= 8, ! kwrk>=5+mu+mv+nuest+nvest, ! lwrk >= 12+nuest*(mv+nvest+3)+nvest*24+4*mu+8*mv+ ! max(nuest,mv+nvest) ! 0< u(i-1)<u(i)< pi,i=2,..,mu, ! -pi<=v(1)< pi, v(1)<v(i-1)<v(i)<v(1)+2*pi, i=3,...,mv ! if iopt(1)=-1: 8<=nu<=min(nuest,mu+6+iopt(2)+iopt(3)) ! 0<tu(5)<tu(6)<...<tu(nu-4)< pi ! 8<=nv<=min(nvest,mv+7) ! v(1)<tv(5)<tv(6)<...<tv(nv-4)<v(1)+2*pi ! the schoenberg-whitney conditions, i.e. there must ! be subset of grid co-ordinates uu(p) and vv(q) such ! that tu(p) < uu(p) < tu(p+4) ,p=1,...,nu-4 ! (iopt(2)=1 and iopt(3)=1 also count for a uu-value ! tv(q) < vv(q) < tv(q+4) ,q=1,...,nv-4 ! (vv(q) is either a value v(j) or v(j)+2*pi) ! if iopt(1)>=0: s>=0 ! if s=0: nuest>=mu+6+iopt(2)+iopt(3), nvest>=mv+7 ! if one of these conditions is found to be violated,control ! is immediately repassed to the calling program. in that ! case there is no approximation returned. ! ! further comments: ! spgrid does not allow individual weighting of the data-values. ! so, if these were determined to widely different accuracies, then ! perhaps the general data set routine sphere should rather be used ! in spite of efficiency. ! by means of the parameter s, the user can control the tradeoff ! between closeness of fit and smoothness of fit of the approximation. ! if s is too large, the spline will be too smooth and signal will be ! lost ; if s is too small the spline will pick up too much noise. in ! the extreme cases the program will return an interpolating spline if ! s=0 and the constrained least-squares polynomial(degrees 3,0)if s is ! very large. between these extremes, a properly chosen s will result ! in a good compromise between closeness of fit and smoothness of fit. ! to decide whether an approximation, corresponding to a certain s is ! satisfactory the user is highly recommended to inspect the fits ! graphically. ! recommended values for s depend on the accuracy of the data values. ! if the user has an idea of the statistical errors on the data, he ! can also find a proper estimate for s. for, by assuming that, if he ! specifies the right s, spgrid will return a spline s(u,v) which ! exactly reproduces the function underlying the data he can evaluate ! the sum((r(i,j)-s(u(i),v(j)))**2) to find a good estimate for this s ! for example, if he knows that the statistical errors on his r(i,j)- ! values is not greater than 0.1, he may expect that a good s should ! have a value not larger than mu*mv*(0.1)**2. ! if nothing is known about the statistical error in r(i,j), s must ! be determined by trial and error, taking account of the comments ! above. the best is then to start with a very large value of s (to ! determine the least-squares polynomial and the corresponding upper ! bound fp0 for s) and then to progressively decrease the value of s ! ( say by a factor 10 in the beginning, i.e. s=fp0/10,fp0/100,... ! and more carefully as the approximation shows more detail) to ! obtain closer fits. ! to economize the search for a good s-value the program provides with ! different modes of computation. at the first call of the routine, or ! whenever he wants to restart with the initial set of knots the user ! must set iopt(1)=0. ! if iopt(1) = 1 the program will continue with the knots found at ! the last call of the routine. this will save a lot of computation ! time if spgrid is called repeatedly for different values of s. ! the number of knots of the spline returned and their location will ! depend on the value of s and on the complexity of the shape of the ! function underlying the data. if the computation mode iopt(1) = 1 ! is used, the knots returned may also depend on the s-values at ! previous calls (if these were smaller). therefore, if after a number ! of trials with different s-values and iopt(1)=1,the user can finally ! accept a fit as satisfactory, it may be worthwhile for him to call ! spgrid once more with the chosen value for s but now with iopt(1)=0. ! indeed, spgrid may then return an approximation of the same quality ! of fit but with fewer knots and therefore better if data reduction ! is also an important objective for the user. ! the number of knots may also depend on the upper bounds nuest and ! nvest. indeed, if at a certain stage in spgrid the number of knots ! in one direction (say nu) has reached the value of its upper bound ! (nuest), then from that moment on all subsequent knots are added ! in the other (v) direction. this may indicate that the value of ! nuest is too small. on the other hand, it gives the user the option ! of limiting the number of knots the routine locates in any direction ! for example, by setting nuest=8 (the lowest allowable value for ! nuest), the user can indicate that he wants an approximation which ! is a simple cubic polynomial in the variable u. ! ! other subroutines required: ! fpspgr,fpchec,fpchep,fpknot,fpopsp,fprati,fpgrsp,fpsysy,fpback, ! fpbacp,fpbspl,fpcyt1,fpcyt2,fpdisc,fpgivs,fprota ! ! references: ! dierckx p. : fast algorithms for smoothing data over a disc or a ! sphere using tensor product splines, in "algorithms ! for approximation", ed. j.c.mason and m.g.cox, ! clarendon press oxford, 1987, pp. 51-65 ! dierckx p. : fast algorithms for smoothing data over a disc or a ! sphere using tensor product splines, report tw73, dept. ! computer science,k.u.leuven, 1985. ! dierckx p. : curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author: ! p.dierckx ! dept. computer science, k.u. leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! creation date : july 1985 ! latest update : march 1989 ! ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: r0,r1,s real(RKIND), intent(inout) :: fp integer, intent(in) :: mu,mv,nuest,nvest,lwrk,kwrk integer, intent(inout) :: nu,nv integer, intent(out) :: ier ! ..array arguments.. integer, intent(in) :: iopt(3),ider(4) integer, intent(inout) :: iwrk(kwrk) real(RKIND), intent(in) :: u(mu),v(mv),r(mu*mv) real(RKIND), intent(inout) :: c((nuest-4)*(nvest-4)),tu(nuest),tv(nvest),wrk(lwrk) ! ..local scalars.. real(RKIND) :: uu,ve,rmax,rmin,rb,re,rn integer :: i,jwrk,kndu,kndv,knru,knrv,kwest,l,ldr,lfpu,lfpv,lwest,lww,m,mumin,muu,nc ! set constants real(RKIND), parameter :: per = pi2 real(RKIND), parameter :: tol = smallnum03 integer, parameter :: maxit = 20 ve = v(1)+per mumin = 4 m = mu*mv nc = (nuest-4)*(nvest-4) lwest = 12+nuest*(mv+nvest+3)+24*nvest+4*mu+8*mv+max(nuest,mv+nvest) kwest = 5+mu+mv+nuest+nvest ! before starting computations, a data check is made. if the input data ! are invalid, control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR if (iopt(1)<(-1) .or. iopt(1)>1) return if (iopt(2)<0 .or. iopt(2)>1) return if (iopt(3)<0 .or. iopt(3)>1) return if (ider(1)<(-1) .or. ider(1)>1) return if (ider(2)<0 .or. ider(2)>1) return if (ider(2)==1 .and. iopt(2)==0) return if (ider(3)<(-1) .or. ider(3)>1) return if (ider(4)<0 .or. ider(4)>1) return if (ider(4)==1 .and. iopt(3)==0) return if (ider(1)>=0) mumin = mumin-1 if (iopt(2)==1 .and. ider(2)==1) mumin = mumin-1 if (ider(3)>=0) mumin = mumin-1 if (iopt(3)==1 .and. ider(4)==1) mumin = mumin-1 if (mumin==0) mumin = 1 if (mu<mumin .or. mv<4) return if (nuest<8 .or. nvest<8) return if (lwrk<lwest .or. kwrk<kwest) return if (u(1)<=zero .or. u(mu)>=pi) return if (mu>1 .and. any(u(1:mu-1)>=u(2:mu))) return if (v(1)< (-pi) .or. v(1)>=pi ) return if (v(mv)>=v(1)+per) return if (any(v(1:mv-1)>=v(2:mv))) return if (iopt(1)<=0) then rn = one/mv ! if not given, we compute an estimate for r0. if (ider(1)>=0) then rb = r0 else rb = sum(r(1:mv))*rn endif ! if not given, we compute an estimate for r1. if (ider(3)>=0) then re = r1 else re = sum(r(m-mv+1:m))*rn endif ! we determine the range of r-values. rmin = min(rb,minval(r(:m))) rmax = max(re,maxval(r(:m))) wrk(5) = rb wrk(6) = zero wrk(7) = zero wrk(8) = re wrk(9) = zero wrk(10) = zero wrk(11) = rmax -rmin wrk(12) = wrk(11) iwrk(4) = mu iwrk(5) = mu endif if (iopt(1)<0) then if (nu<8 .or. nu>nuest) return if (nv<11 .or. nv>nvest) return ! Init tu: use wrk(13:) as working space tu(1:4) = zero tu(nu-3:nu) = pi l = 13 wrk(l) = zero if (iopt(2)/=0) then l = l+1 uu = min(u(1), tu(5)) wrk(l) = uu*half endif do i=1,mu l = l+1 wrk(l) = u(i) end do if (iopt(3)/=0) then l = l+1 uu = max(u(mu), tu(nu-4)) wrk(l) = uu+(pi-uu)*half endif l = l+1 wrk(l) = pi muu = l-12 ier = fpchec(wrk(13),muu,tu,nu,3); if (ier/=FITPACK_OK) return ! Init tv: use wrk(13:) as working space tv(1:4) = [tv(nv-6:nv-4)-per, v(1)] tv(nv-3:nv) = [ve, tv(5:7)+per] wrk(13:13+mv) = [v, ve] ier = fpchep(wrk(13),mv+1,tv,nv,3); if (ier/=FITPACK_OK) return else if (s<zero) return if (equal(s,zero) .and. (nuest<(mu+6+iopt(2)+iopt(3)) .or. nvest<(mv+7)) ) return endif ! we partition the working space and determine the spline approximation ldr = 5 lfpu = 13 lfpv = lfpu+nuest lww = lfpv+nvest jwrk = lwrk-12-nuest-nvest knru = 6 knrv = knru+mu kndu = knrv+mv kndv = kndu+nuest call fpspgr(iopt,ider,u,mu,v,mv,r,m,rb,re,s,nuest,nvest,tol,maxit, & nc,nu,tu,nv,tv,c,fp,wrk(1),wrk(2),wrk(3),wrk(4),wrk(lfpu), & wrk(lfpv),wrk(ldr),wrk(11),iwrk(1),iwrk(2),iwrk(3),iwrk(4), & iwrk(5),iwrk(knru),iwrk(knrv),iwrk(kndu),iwrk(kndv),wrk(lww), & jwrk,ier) return end subroutine spgrid pure subroutine sphere(iopt,m,teta,phi,r,w,s,ntest,npest, & eps,nt,tt,np,tp,c,fp,wrk1,lwrk1,wrk2,lwrk2,iwrk,kwrk,ier) ! subroutine sphere determines a smooth bicubic spherical spline ! approximation s(teta,phi), 0 <= teta <= pi ; 0 <= phi <= 2*pi ! to a given set of data points (teta(i),phi(i),r(i)),i=1,2,...,m. ! such a spline has the following specific properties ! ! (1) s(0,phi) = constant 0 <=phi<= 2*pi. ! ! (2) s(pi,phi) = constant 0 <=phi<= 2*pi ! ! j j ! d s(teta,0) d s(teta,2*pi) ! (3) ----------- = ------------ 0 <=teta<=pi, j=0,1,2 ! j j ! d phi d phi ! ! d s(0,phi) d s(0,0) d s(0,pi/2) ! (4) ---------- = -------- *cos(phi) + ----------- *sin(phi) ! d teta d teta d teta ! ! d s(pi,phi) d s(pi,0) d s(pi,pi/2) ! (5) ----------- = ---------*cos(phi) + ------------*sin(phi) ! d teta d teta d teta ! ! if iopt =-1 sphere calculates a weighted least-squares spherical ! spline according to a given set of knots in teta- and phi- direction. ! if iopt >=0, the number of knots in each direction and their position ! tt(j),j=1,2,...,nt ; tp(j),j=1,2,...,np are chosen automatically by ! the routine. the smoothness of s(teta,phi) is then achieved by mini- ! malizing the discontinuity jumps of the derivatives of the spline ! at the knots. the amount of smoothness of s(teta,phi) is determined ! by the condition that fp = sum((w(i)*(r(i)-s(teta(i),phi(i))))**2) ! be <= s, with s a given non-negative constant. ! the spherical spline is given in the standard b-spline representation ! of bicubic splines and can be evaluated by means of subroutine bispev ! ! calling sequence: ! call sphere(iopt,m,teta,phi,r,w,s,ntest,npest,eps, ! * nt,tt,np,tp,c,fp,wrk1,lwrk1,wrk2,lwrk2,iwrk,kwrk,ier) ! ! parameters: ! iopt : integer flag. on entry iopt must specify whether a weighted ! least-squares spherical spline (iopt=-1) or a smoothing ! spherical spline (iopt=0 or 1) must be determined. ! if iopt=0 the routine will start with an initial set of knots ! tt(i)=0,tt(i+4)=pi,i=1,...,4;tp(i)=0,tp(i+4)=2*pi,i=1,...,4. ! if iopt=1 the routine will continue with the set of knots ! found at the last call of the routine. ! attention: a call with iopt=1 must always be immediately pre- ! ceded by another call with iopt=1 or iopt=0. ! unchanged on exit. ! m : integer. on entry m must specify the number of data points. ! m >= 2. unchanged on exit. ! teta : real array of dimension at least (m). ! phi : real array of dimension at least (m). ! r : real array of dimension at least (m). ! before entry,teta(i),phi(i),r(i) must be set to the spherical ! co-ordinates of the i-th data point, for i=1,...,m.the order ! of the data points is immaterial. unchanged on exit. ! w : real array of dimension at least (m). before entry, w(i) must ! be set to the i-th value in the set of weights. the w(i) must ! be strictly positive. unchanged on exit. ! s : real. on entry (in case iopt>=0) s must specify the smoothing ! factor. s >=0. unchanged on exit. ! for advice on the choice of s see further comments ! ntest : integer. unchanged on exit. ! npest : integer. unchanged on exit. ! on entry, ntest and npest must specify an upper bound for the ! number of knots required in the teta- and phi-directions. ! these numbers will also determine the storage space needed by ! the routine. ntest >= 8, npest >= 8. ! in most practical situation ntest = npest = 8+sqrt(m/2) will ! be sufficient. see also further comments. ! eps : real. ! on entry, eps must specify a threshold for determining the ! effective rank of an over-determined linear system of equat- ! ions. 0 < eps < 1. if the number of decimal digits in the ! computer representation of a real number is q, then 10**(-q) ! is a suitable value for eps in most practical applications. ! unchanged on exit. ! nt : integer. ! unless ier=10 (in case iopt >=0), nt will contain the total ! number of knots with respect to the teta-variable, of the ! spline approximation returned. if the computation mode iopt=1 ! is used, the value of nt should be left unchanged between ! subsequent calls. ! in case iopt=-1, the value of nt should be specified on entry ! tt : real array of dimension at least ntest. ! on successful exit, this array will contain the knots of the ! spline with respect to the teta-variable, i.e. the position ! of the interior knots tt(5),...,tt(nt-4) as well as the ! position of the additional knots tt(1)=...=tt(4)=0 and ! tt(nt-3)=...=tt(nt)=pi needed for the b-spline representation ! if the computation mode iopt=1 is used, the values of tt(1), ! ...,tt(nt) should be left unchanged between subsequent calls. ! if the computation mode iopt=-1 is used, the values tt(5), ! ...tt(nt-4) must be supplied by the user, before entry. ! see also the restrictions (ier=10). ! np : integer. ! unless ier=10 (in case iopt >=0), np will contain the total ! number of knots with respect to the phi-variable, of the ! spline approximation returned. if the computation mode iopt=1 ! is used, the value of np should be left unchanged between ! subsequent calls. ! in case iopt=-1, the value of np (>=9) should be specified ! on entry. ! tp : real array of dimension at least npest. ! on successful exit, this array will contain the knots of the ! spline with respect to the phi-variable, i.e. the position of ! the interior knots tp(5),...,tp(np-4) as well as the position ! of the additional knots tp(1),...,tp(4) and tp(np-3),..., ! tp(np) needed for the b-spline representation. ! if the computation mode iopt=1 is used, the values of tp(1), ! ...,tp(np) should be left unchanged between subsequent calls. ! if the computation mode iopt=-1 is used, the values tp(5), ! ...tp(np-4) must be supplied by the user, before entry. ! see also the restrictions (ier=10). ! c : real array of dimension at least (ntest-4)*(npest-4). ! on successful exit, c contains the coefficients of the spline ! approximation s(teta,phi). ! fp : real. unless ier=10, fp contains the weighted sum of ! squared residuals of the spline approximation returned. ! wrk1 : real array of dimension (lwrk1). used as workspace. ! if the computation mode iopt=1 is used the value of wrk1(1) ! should be left unchanged between subsequent calls. ! on exit wrk1(2),wrk1(3),...,wrk1(1+ncof) will contain the ! values d(i)/max(d(i)),i=1,...,ncof=6+(np-7)*(nt-8) ! with d(i) the i-th diagonal element of the reduced triangular ! matrix for calculating the b-spline coefficients. it includes ! those elements whose square is less than eps,which are treat- ! ed as 0 in the case of presumed rank deficiency (ier<-2). ! lwrk1 : integer. on entry lwrk1 must specify the actual dimension of ! the array wrk1 as declared in the calling (sub)program. ! lwrk1 must not be too small. let ! u = ntest-7, v = npest-7, then ! lwrk1 >= 185+52*v+10*u+14*u*v+8*(u-1)*v**2+8*m ! wrk2 : real array of dimension (lwrk2). used as workspace, but ! only in the case a rank deficient system is encountered. ! lwrk2 : integer. on entry lwrk2 must specify the actual dimension of ! the array wrk2 as declared in the calling (sub)program. ! lwrk2 > 0 . a save upper bound for lwrk2 = 48+21*v+7*u*v+ ! 4*(u-1)*v**2 where u,v are as above. if there are enough data ! points, scattered uniformly over the approximation domain ! and if the smoothing factor s is not too small, there is a ! good chance that this extra workspace is not needed. a lot ! of memory might therefore be saved by setting lwrk2=1. ! (see also ier > 10) ! iwrk : integer array of dimension (kwrk). used as workspace. ! kwrk : integer. on entry kwrk must specify the actual dimension of ! the array iwrk as declared in the calling (sub)program. ! kwrk >= m+(ntest-7)*(npest-7). ! ier : integer. unless the routine detects an error, ier contains a ! non-positive value on exit, i.e. ! ier=0 : normal return. the spline returned has a residual sum of ! squares fp such that abs(fp-s)/s <= tol with tol a relat- ! ive tolerance set to 0.001 by the program. ! ier=-1 : normal return. the spline returned is a spherical ! interpolating spline (fp=0). ! ier=-2 : normal return. the spline returned is the weighted least- ! squares constrained polynomial . in this extreme case ! fp gives the upper bound for the smoothing factor s. ! ier<-2 : warning. the coefficients of the spline returned have been ! computed as the minimal norm least-squares solution of a ! (numerically) rank deficient system. (-ier) gives the rank. ! especially if the rank deficiency which can be computed as ! 6+(nt-8)*(np-7)+ier, is large the results may be inaccurate ! they could also seriously depend on the value of eps. ! ier=1 : error. the required storage space exceeds the available ! storage space, as specified by the parameters ntest and ! npest. ! probably causes : ntest or npest too small. if these param- ! eters are already large, it may also indicate that s is ! too small ! the approximation returned is the weighted least-squares ! spherical spline according to the current set of knots. ! the parameter fp gives the corresponding weighted sum of ! squared residuals (fp>s). ! ier=2 : error. a theoretically impossible result was found during ! the iteration process for finding a smoothing spline with ! fp = s. probably causes : s too small or badly chosen eps. ! there is an approximation returned but the corresponding ! weighted sum of squared residuals does not satisfy the ! condition abs(fp-s)/s < tol. ! ier=3 : error. the maximal number of iterations maxit (set to 20 ! by the program) allowed for finding a smoothing spline ! with fp=s has been reached. probably causes : s too small ! there is an approximation returned but the corresponding ! weighted sum of squared residuals does not satisfy the ! condition abs(fp-s)/s < tol. ! ier=4 : error. no more knots can be added because the dimension ! of the spherical spline 6+(nt-8)*(np-7) already exceeds ! the number of data points m. ! probably causes : either s or m too small. ! the approximation returned is the weighted least-squares ! spherical spline according to the current set of knots. ! the parameter fp gives the corresponding weighted sum of ! squared residuals (fp>s). ! ier=5 : error. no more knots can be added because the additional ! knot would (quasi) coincide with an old one. ! probably causes : s too small or too large a weight to an ! inaccurate data point. ! the approximation returned is the weighted least-squares ! spherical spline according to the current set of knots. ! the parameter fp gives the corresponding weighted sum of ! squared residuals (fp>s). ! ier=10 : error. on entry, the input data are controlled on validity ! the following restrictions must be satisfied. ! -1<=iopt<=1, m>=2, ntest>=8 ,npest >=8, 0<eps<1, ! 0<=teta(i)<=pi, 0<=phi(i)<=2*pi, w(i)>0, i=1,...,m ! lwrk1 >= 185+52*v+10*u+14*u*v+8*(u-1)*v**2+8*m ! kwrk >= m+(ntest-7)*(npest-7) ! if iopt=-1: 8<=nt<=ntest , 9<=np<=npest ! 0<tt(5)<tt(6)<...<tt(nt-4)<pi ! 0<tp(5)<tp(6)<...<tp(np-4)<2*pi ! if iopt>=0: s>=0 ! if one of these conditions is found to be violated,control ! is immediately repassed to the calling program. in that ! case there is no approximation returned. ! ier>10 : error. lwrk2 is too small, i.e. there is not enough work- ! space for computing the minimal least-squares solution of ! a rank deficient system of linear equations. ier gives the ! requested value for lwrk2. there is no approximation re- ! turned but, having saved the information contained in nt, ! np,tt,tp,wrk1, and having adjusted the value of lwrk2 and ! the dimension of the array wrk2 accordingly, the user can ! continue at the point the program was left, by calling ! sphere with iopt=1. ! ! further comments: ! by means of the parameter s, the user can control the tradeoff ! between closeness of fit and smoothness of fit of the approximation. ! if s is too large, the spline will be too smooth and signal will be ! lost ; if s is too small the spline will pick up too much noise. in ! the extreme cases the program will return an interpolating spline if ! s=0 and the constrained weighted least-squares polynomial if s is ! very large. between these extremes, a properly chosen s will result ! in a good compromise between closeness of fit and smoothness of fit. ! to decide whether an approximation, corresponding to a certain s is ! satisfactory the user is highly recommended to inspect the fits ! graphically. ! recommended values for s depend on the weights w(i). if these are ! taken as 1/d(i) with d(i) an estimate of the standard deviation of ! r(i), a good s-value should be found in the range (m-sqrt(2*m),m+ ! sqrt(2*m)). if nothing is known about the statistical error in r(i) ! each w(i) can be set equal to one and s determined by trial and ! error, taking account of the comments above. the best is then to ! start with a very large value of s ( to determine the least-squares ! polynomial and the corresponding upper bound fp0 for s) and then to ! progressively decrease the value of s ( say by a factor 10 in the ! beginning, i.e. s=fp0/10, fp0/100,...and more carefully as the ! approximation shows more detail) to obtain closer fits. ! to choose s very small is strongly discouraged. this considerably ! increases computation time and memory requirements. it may also ! cause rank-deficiency (ier<-2) and endager numerical stability. ! to economize the search for a good s-value the program provides with ! different modes of computation. at the first call of the routine, or ! whenever he wants to restart with the initial set of knots the user ! must set iopt=0. ! if iopt=1 the program will continue with the set of knots found at ! the last call of the routine. this will save a lot of computation ! time if sphere is called repeatedly for different values of s. ! the number of knots of the spline returned and their location will ! depend on the value of s and on the complexity of the shape of the ! function underlying the data. if the computation mode iopt=1 ! is used, the knots returned may also depend on the s-values at ! previous calls (if these were smaller). therefore, if after a number ! of trials with different s-values and iopt=1, the user can finally ! accept a fit as satisfactory, it may be worthwhile for him to call ! sphere once more with the selected value for s but now with iopt=0. ! indeed, sphere may then return an approximation of the same quality ! of fit but with fewer knots and therefore better if data reduction ! is also an important objective for the user. ! the number of knots may also depend on the upper bounds ntest and ! npest. indeed, if at a certain stage in sphere the number of knots ! in one direction (say nt) has reached the value of its upper bound ! (ntest), then from that moment on all subsequent knots are added ! in the other (phi) direction. this may indicate that the value of ! ntest is too small. on the other hand, it gives the user the option ! of limiting the number of knots the routine locates in any direction ! for example, by setting ntest=8 (the lowest allowable value for ! ntest), the user can indicate that he wants an approximation which ! is a cubic polynomial in the variable teta. ! ! other subroutines required: ! fpback,fpbspl,fpsphe,fpdisc,fpgivs,fprank,fprati,fprota,fporde, ! fprpsp ! ! references: ! dierckx p. : algorithms for smoothing data on the sphere with tensor ! product splines, computing 32 (1984) 319-342. ! dierckx p. : algorithms for smoothing data on the sphere with tensor ! product splines, report tw62, dept. computer science, ! k.u.leuven, 1983. ! dierckx p. : curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author: ! p.dierckx ! dept. computer science, k.u. leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! creation date : july 1983 ! latest update : march 1989 ! ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: s,eps real(RKIND), intent(inout) :: fp integer, intent(in) :: iopt,m,ntest,npest,lwrk1,lwrk2,kwrk integer, intent(inout) :: nt,np integer, intent(out) :: ier ! ..array arguments.. real(RKIND), intent(in) :: teta(m),phi(m),r(m),w(m) real(RKIND), intent(inout) :: tt(ntest),tp(npest),c((ntest-4)*(npest-4)),wrk1(lwrk1),wrk2(lwrk2) integer, intent(inout) :: iwrk(kwrk) ! ..local scalars.. integer :: i,ib1,ib3,ki,kn,kwest,la,lbt,lcc,lcs,lro,j,lbp,lco,lf,lff,lfp,lh,lq,lst,lsp,lwest, & ncest,ncc,ntt,npp,nreg,nrint,ncof,nt4,np4 ! we set up the parameters tol and maxit. real(RKIND), parameter :: tol = smallnum03 integer , parameter :: maxit = 20 ! before starting computations a data check is made. if the input data ! are invalid,control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR nt4 = ntest-4 np4 = npest-4 ncest = nt4*np4 ntt = ntest-7 npp = npest-7 ncc = 6+npp*(ntt-1) nrint = ntt+npp nreg = ntt*npp ncof = 6+3*npp ib1 = max(4*npp, ncof) ib3 = max(ib1+3, ncof) lwest = 185+52*npp+10*ntt+14*ntt*npp+8*(m+(ntt-1)*npp**2) kwest = m+nreg if (.not.(eps>0 .and. eps<one)) return if (iopt<(-1) .or. iopt>1) return if (m<2) return if (ntest<8 .or. npest<8) return if (lwrk1<lwest .or. kwrk<kwest) return if (iopt<=0) then if (any(w<=zero)) return if (any(teta<zero .or. teta>pi)) return if (any(phi<zero .or. phi>pi2)) return endif if (iopt<0) then ! Check teta knots monotonic ntt = nt-8 if (ntt<0 .or. nt>ntest) return if (ntt>0) then tt(4) = zero do i=1,ntt j = i+4 if(tt(j)<=tt(j-1) .or. tt(j)>=pi) return end do endif ! Check phi knots monotonic npp = np-8 if (npp<1 .or. np>npest) return tp(4) = zero do i=1,npp j = i+4 if(tp(j)<=tp(j-1) .or. tp(j)>=pi2) return end do endif if (iopt>=0 .and. s<zero) return ier = FITPACK_OK ! we partition the working space and determine the spline approximation kn = 1 ki = kn+m lq = 2 la = lq+ncc*ib3 lf = la+ncc*ib1 lff = lf+ncc lfp = lff+ncest lco = lfp+nrint lh = lco+nrint lbt = lh+ib3 lbp = lbt+5*ntest lro = lbp+5*npest lcc = lro+npest lcs = lcc+npest lst = lcs+npest lsp = lst+m*4 call fpsphe(iopt,m,teta,phi,r,w,s,ntest,npest,eps,tol,maxit, & ib1,ib3,ncest,ncc,nrint,nreg,nt,tt,np,tp,c,fp,wrk1(1),wrk1(lfp), & wrk1(lco),wrk1(lf),wrk1(lff),wrk1(lro),wrk1(lcc),wrk1(lcs), & wrk1(la),wrk1(lq),wrk1(lbt),wrk1(lbp),wrk1(lst),wrk1(lsp), & wrk1(lh),iwrk(ki),iwrk(kn),wrk2,lwrk2,ier) return end subroutine sphere ! subroutine splder evaluates in a number of points x(i),i=1,2,...,m the derivative of order nu ! of a spline s(x) of degree k,given in its b-spline representation. pure subroutine splder(t,n,c,k,nu,x,y,m,e,wrk,ier) ! ! calling sequence: ! call splder(t,n,c,k,nu,x,y,m,e,wrk,ier) ! ! input parameters: ! t : array,length n, which contains the position of the knots. ! n : integer, giving the total number of knots of s(x). ! c : array,length n, which contains the b-spline coefficients. ! k : integer, giving the degree of s(x). ! nu : integer, specifying the order of the derivative. 0<=nu<=k ! x : array,length m, which contains the points where the derivative of s(x) must be evaluated. ! m : integer, giving the number of points where the derivative of s(x) must be evaluated ! e : integer, if 0 the spline is extrapolated from the end spans for points not in the ! support, if 1 the spline evaluates to zero for those points, and if 2 ier is set to ! 1 and the subroutine returns. ! wrk : real array of dimension n. used as working space. ! ! output parameters: ! y : array,length m, giving the value of the derivative of s(x) at the different points. ! ier : error flag ! ier = 0 : normal return ! ier = 1 : argument out of bounds and e == 2 ! ier =10 : invalid input data (see restrictions) ! ! restrictions: ! 0 <= nu <= k ! m >= 1 ! t(k+1) <= x(i) <= x(i+1) <= t(n-k) , i=1,2,...,m-1. ! ! other subroutines required: fpbspl ! ! references : ! de boor c : on calculating with b-splines, j. approximation theory 6 (1972) 50-62. ! cox m.g. : the numerical evaluation of b-splines, j. inst. maths applics 10 (1972) 134-149. ! dierckx p. : curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! latest update : march 1987 ! !++ pearu: 13 aug 2003 !++ - disabled cliping x values to interval [min(t),max(t)] !++ - removed the restriction of the orderness of x values !++ - fixed initialization of sp to real(RKIND) value ! ! ..scalar arguments.. integer, intent(in) :: n,k,nu,m,e integer, intent(out) :: ier ! ..array arguments.. real(RKIND), intent(in) :: t(n),c(n),x(m) real(RKIND), intent(out) :: y(m) real(RKIND), intent(inout) :: wrk(n) ! ..local scalars.. integer :: i,j,k1,k2,k3,l,l1,l2,nk1,nk2,kk real(RKIND) :: ak,arg,fac,tb,te ! ..local arrays .. real(RKIND) :: h(MAX_ORDER+1) logical :: nonflat ! before starting computations a data check is made. if the input data ! are invalid control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR if (nu<0 .or. nu>k) return if (m<1) return kk = k-nu ier = FITPACK_OK ! fetch tb and te, the boundaries of the approximation interval. k1 = k+1 k3 = k1+1 nk1 = n-k1 tb = t(k1) te = t(nk1+1) ! the derivative of order nu of a spline of degree k is a spline of ! degree k-nu,the b-spline coefficients wrk(i) of which can be found ! using the recurrence scheme of de boor. l = 1 wrk(1:nk1) = c(1:nk1) if (nu/=0) then nk2 = nk1 de_boor: do j=1,nu ak = k1-j nk2 = nk2-1 l1 = l do i=1,nk2 l1 = l1+1 l2 = l1+k1-j fac = t(l2)-t(l1) if (fac>zero) wrk(i) = ak*(wrk(i+1)-wrk(i))/fac end do l = l+1 end do de_boor endif nonflat = nu==0 .or. k/=nu l = k1 l1 = l+1 k2 = k1-nu j = 1 ! main loop for the different points. user_points: do i=1,m ! fetch a new x-value arg. arg = x(i) ! check if arg is in the support if (arg < tb .or. arg > te) then select case (e) case (OUTSIDE_EXTRAPOLATE) ! continue like any other point case (OUTSIDE_ZERO) y(i) = zero cycle user_points case (OUTSIDE_NOT_ALLOWED) ier = FITPACK_INSUFFICIENT_STORAGE return end select endif ! search for knot interval t(l) <= arg < t(l+1) do while (.not.(arg>=t(l) .or. l1==k3)) l1 = l l = l-1 j = j-1 end do ! ++ do while (.not.(arg<t(l1) .or. l==nk1)) l = l1 l1 = l+1 j = j+1 end do if (nonflat) then ! evaluate the non-zero b-splines of degree k-nu at arg. h = fpbspl(t,n,kk,arg,l) ! find the value of the derivative at x=arg. y(i) = dot_product(h(1:k2),wrk(l-k:l-nu)) else ! if nu=k the derivative is a piecewise constant function y(i) = wrk(j) endif end do user_points return end subroutine splder ! subroutine splev evaluates in a number of points x(i),i=1,2,...,m a spline s(x) of degree k, ! given in its b-spline representation. pure subroutine splev(t,n,c,k,x,y,m,e,ier) ! calling sequence: ! call splev(t,n,c,k,x,y,m,e,ier) ! ! input parameters: ! t : array,length n, which contains the position of the knots. ! n : integer, giving the total number of knots of s(x). ! c : array,length n, which contains the b-spline coefficients. ! k : integer, giving the degree of s(x). ! x : array,length m, which contains the points where s(x) must be evaluated. ! m : integer, giving the number of points where s(x) must be evaluated. ! e : integer, boundary condition for points outside the support ! 0 = the spline is extrapolated from the end spans ! 1 = the spline evaluates to zero for those points, ! 2 = extrapolation not allowed, ier is set to 1 and the subroutine returns, ! 3 = the spline evaluates to the value of the nearest boundary point. ! ! output parameter: ! y : array,length m, giving the value of s(x) at the different points. ! ier : error flag ! ! restrictions: ! m >= 1 ! ! other subroutines required: fpbspl. ! ! references : ! de boor c : on calculating with b-splines, j. approximation theory 6 (1972) 50-62. ! cox m.g. : the numerical evaluation of b-splines, j. inst. maths applics 10 (1972) 134-149. ! dierckx p. : curve and surface fitting with splines, monographs on numerical analysis, oxford ! university press, 1993. ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! ..scalar arguments.. integer, intent(in) :: n, k, m, e integer, intent(out) :: ier ! ..array arguments.. real(RKIND), intent(in) :: t(n), c(n), x(m) real(RKIND), intent(out) :: y(m) ! ..local scalars.. integer :: i, k1, l, l1, nk1,k2 real(RKIND) :: arg, tb, te ! ..local array.. real(RKIND) :: h(MAX_ORDER+1) ! .. ! before starting computations a data check is made. if the input data ! are invalid control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR if (m<1) return ier = FITPACK_OK ! fetch tb and te, the boundaries of the approximation interval. k1 = k + 1 k2 = k1 + 1 nk1 = n - k1 tb = t(k1) te = t(nk1 + 1) l = k1 l1 = l + 1 ! main loop for the different points. user_points: do i = 1, m ! fetch a new x-value arg. arg = x(i) ! check if arg is in the support outside: if (arg<tb .or. arg>te) then select case (e) case (OUTSIDE_EXTRAPOLATE) ! Continue normally case (OUTSIDE_ZERO) y(i) = zero cycle user_points case (OUTSIDE_NOT_ALLOWED) ier = FITPACK_INVALID_RANGE return case (OUTSIDE_NEAREST_BND) arg = max(min(arg,te),tb) end select endif outside ! search for knot interval t(l) <= arg < t(l+1) do while (arg<t(l) .and. l1/=k2) l1 = l l = l - 1 end do do while (arg>=t(l1) .and. l/=nk1) l = l1 l1 = l + 1 end do ! evaluate the non-zero b-splines at arg. h = fpbspl(t, n, k, arg, l) ! find the value of s(x) at x=arg. y(i) = dot_product(c(l-k:l),h(1:k1)) end do user_points end subroutine splev ! function splint calculates the integral of a spline function s(x) ! of degree k, which is given in its normalized b-spline representation real(RKIND) function splint(t,n,c,k,a,b,wrk) result(integral) ! calling sequence: ! aint = splint(t,n,c,k,a,b,wrk) ! ! input parameters: ! t : array,length n,which contains the position of the knots of s(x). ! n : integer, giving the total number of knots of s(x). ! c : array,length n, containing the b-spline coefficients. ! k : integer, giving the degree of s(x). ! a,b : real values, containing the end points of the integration interval. s(x) is considered to be identically ! zero outside the interval (t(k+1),t(n-k)). ! ! output parameter: ! aint : real, containing the integral of s(x) between a and b. ! wrk : real array, length n, used as working space. on output, wrk will contain the integrals of the normalized ! b-splines defined on the set of knots. ! ! other subroutines required: fpintb. ! ! references : ! gaffney p.w. : the calculation of indefinite integrals of b-splines. j. inst. maths applics 17 (1976) 37-41. ! dierckx p. : curve and surface fitting with splines, monographs on numerical analysis, oxford university press, 1993. ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! ..scalar arguments.. real(RKIND), intent(in) :: a,b integer , intent(in) :: n,k ! ..array arguments.. real(RKIND), intent(in) :: t(n),c(n) real(RKIND), intent(inout) :: wrk(n) ! ..local scalars.. integer :: nk1 nk1 = n-k-1 ! calculate the integrals wrk(i) of the normalized b-splines ni,k+1(x), i=1,2,...nk1. call fpintb(t,n,wrk,nk1,a,b) ! calculate the integral of s(x). integral = dot_product(c(1:nk1),wrk(1:nk1)) return end function splint ! subroutine sproot finds the zeros of a cubic spline s(x),which is given in its normalized ! b-spline representation. pure subroutine sproot(t,n,c,zeros,mest,m,ier) ! ! input parameters: ! t : real array,length n, containing the knots of s(x). ! n : integer, containing the number of knots. n>=8 ! c : real array,length n, containing the b-spline coefficients. ! mest : integer, specifying the dimension of array zero. ! ! output parameters: ! zeros : real array,length mest, containing the zeros of s(x). ! m : integer,giving the number of zeros. ! ier : error flag: ! ier = 0: normal return. ! ier = 1: the number of zeros exceeds mest. ! ier =10: invalid input data (see restrictions). ! ! other subroutines required: fpcuro ! ! restrictions: ! 1) n>= 8. ! 2) t(4) < t(5) < ... < t(n-4) < t(n-3). ! t(1) <= t(2) <= t(3) <= t(4) ! t(n-3) <= t(n-2) <= t(n-1) <= t(n) ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! latest update : october 2022 ! ! .. ! ..scalar arguments.. integer, intent(in) :: n,mest integer, intent(out) :: m,ier ! ..array arguments.. real(RKIND), intent(in) :: t(n),c(n) real(RKIND), intent(out) :: zeros(mest) ! ..local scalars.. integer :: i,j,j1,l,n4 real(RKIND) :: ah,a0,a1,a2,a3,bh,b0,b1,c1,c2,c3,c4,c5,d4,d5,h1,h2,t1,t2,t3,t4,t5 logical :: z0,z1,z2,z3,z4,nz0,nz1,nz2,nz3,nz4 ! ..local array.. real(RKIND) :: y(3) ! .. ! before starting computations a data check is made. if the input data ! are invalid, control is immediately repassed to the calling program. n4 = n-4 ier = FITPACK_INPUT_ERROR if(n<8) return j = n do i=1,3 if(t(i)>t(i+1)) return if(t(j)<t(j-1)) return j = j-1 end do if (any(t(4:n4)>=t(5:n4+1))) return ! the problem considered reduces to finding the zeros of the cubic polynomials pl(x) which define ! the cubic spline in each knot interval t(l)<=x<=t(l+1). a zero of pl(x) is also a zero of s(x) on ! the condition that it belongs to the knot interval. the cubic polynomial pl(x) is determined by ! computing s(t(l)), s'(t(l)),s(t(l+1)) and s'(t(l+1)). in fact we only have to compute s(t(l+1)) ! and s'(t(l+1)); because of the continuity conditions of splines and their derivatives, the value ! of s(t(l)) and s'(t(l)) is already known from the foregoing knot interval. ier = FITPACK_OK ! evaluate some constants for the first knot interval h1 = t(4)-t(3) h2 = t(5)-t(4) t1 = t(4)-t(2) t2 = t(5)-t(3) t3 = t(6)-t(4) t4 = t(5)-t(2) t5 = t(6)-t(3) ! calculate a0 = s(t(4)) and ah = s'(t(4)). c1 = c(1) c2 = c(2) c3 = c(3) c4 = (c2-c1)/t4 c5 = (c3-c2)/t5 d4 = (h2*c1+t1*c2)/t4 d5 = (t3*c2+h1*c3)/t5 a0 = (h2*d4+h1*d5)/t2 ah = three*(h2*c4+h1*c5)/t2 z1 = .not.ah<zero nz1 = .not.z1 m = 0 ! main loop for the different knot intervals. knot_intervals: do l=4,n4 ! evaluate some constants for the knot interval t(l) <= x <= t(l+1). h1 = h2 h2 = t(l+2)-t(l+1) t1 = t2 t2 = t3 t3 = t(l+3)-t(l+1) t4 = t5 t5 = t(l+3)-t(l) ! find a0 = s(t(l)), ah = s'(t(l)), b0 = s(t(l+1)) and bh = s'(t(l+1)). c1 = c2 c2 = c3 c3 = c(l) c4 = c5 c5 = (c3-c2)/t5 d4 = (h2*c1+t1*c2)/t4 d5 = (h1*c3+t3*c2)/t5 b0 = (h2*d4+h1*d5)/t2 bh = three*(h2*c4+h1*c5)/t2 ! calculate the coefficients a0,a1,a2 and a3 of the cubic polynomial ! pl(x) = ql(y) = a0+a1*y+a2*y**2+a3*y**3 ; y = (x-t(l))/(t(l+1)-t(l)). a1 = ah*h1 b1 = bh*h1 a2 = three*(b0-a0)-b1-two*a1 a3 = two*(a0-b0)+b1+a1 ! test whether or not pl(x) could have a zero in the range t(l) <= x <= t(l+1). z0 = .not.a0<zero nz0 = .not.z0 z2 = .not.a2<zero nz2 = .not.z2 z3 = .not.b1<zero nz3 = .not.z3 z4 = .not.three*a3+a2<zero nz4 = .not.z4 ! find the zeros of ql(y). zeroes: if (a0*b0<=zero .or. ((z0.and.(nz1.and.(z3.or.z2.and.nz4).or.nz2.and.z3.and.z4) & .or.nz0.and.(z1.and.(nz3.or.nz2.and.z4).or.z2.and.nz3.and.nz4)))) then call fpcuro(a3,a2,a1,a0,y,j) if (j/=0) then ! find which zeros of pl(x) are zeros of s(x). which_zeros: do i=1,j if(y(i)<zero .or. y(i)>one) cycle which_zeros ! test whether the number of zeros of s(x) exceeds mest. if (m>=mest) then ier = FITPACK_INSUFFICIENT_STORAGE return end if m = m+1 zeros(m) = t(l)+h1*y(i) end do which_zeros endif endif zeroes a0 = b0 ah = bh z1 = z3 nz1 = nz3 end do knot_intervals ! the zeros of s(x) are arranged in increasing order. if (m<2) return ! FP this double loop can be made more efficient sort_zeros: do j=1,m inner_loop: do j1 = j+1,m if (zeros(j1)<zeros(j)) call swap_RKIND(zeros(j),zeros(j1)) end do inner_loop end do sort_zeros ! Filter duplicates j = m m = 1 filter_duplicates: do i=2,j if (equal(zeros(i),zeros(m))) cycle filter_duplicates m = m+1 zeros(m) = zeros(i) end do filter_duplicates return end subroutine sproot ! subroutine surev evaluates on a grid (u(i),v(j)),i=1,...,mu; j=1,...,mv a bicubic spline ! surface of dimension idim, given in the b-spline representation. pure subroutine surev(idim,tu,nu,tv,nv,c,u,mu,v,mv,f,mf,wrk,lwrk,iwrk,kwrk,ier) ! ! calling sequence: ! call surev(idim,tu,nu,tv,nv,c,u,mu,v,mv,f,mf,wrk,lwrk,iwrk,kwrk,ier) ! ! input parameters: ! idim : integer, specifying the dimension of the spline surface. ! tu : real array, length nu, which contains the position of the knots in the u-direction. ! nu : integer, giving the total number of knots in the u-direction ! tv : real array, length nv, which contains the position of the knots in the v-direction. ! nv : integer, giving the total number of knots in the v-direction ! c : real array, length (nu-4)*(nv-4)*idim, which contains the b-spline coefficients. ! u : real array of dimension (mu). ! before entry u(i) must be set to the u co-ordinate of the i-th grid point along the u-axis. ! tu(4)<=u(i-1)<=u(i)<=tu(nu-3), i=2,...,mu. ! mu : on entry mu must specify the number of grid points along the u-axis. mu >=1. ! v : real array of dimension (mv). ! before entry v(j) must be set to the v co-ordinate of the j-th grid point along the ! v-axis. tv(4)<=v(j-1)<=v(j)<=tv(nv-3), j=2,...,mv. ! mv : on entry mv must specify the number of grid points along the v-axis. mv >=1. ! mf : on entry, mf must specify the dimension of the array f. mf >= mu*mv*idim ! wrk : real array of dimension lwrk. used as workspace. ! lwrk : integer, specifying the dimension of wrk. lwrk >= 4*(mu+mv) ! iwrk : integer array of dimension kwrk. used as workspace. ! kwrk : integer, specifying the dimension of iwrk. kwrk >= mu+mv. ! ! output parameters: ! f : real array of dimension (mf). ! on successful exit f(mu*mv*(l-1)+mv*(i-1)+j) contains the l-th co-ordinate of the bicubic ! spline surface at the point (u(i),v(j)),l=1,...,idim,i=1,...,mu;j=1,...,mv. ! ier : integer error flag ! ier=0 : normal return ! ier=10: invalid input data (see restrictions) ! ! restrictions: ! mu >=1, mv >=1, lwrk>=4*(mu+mv), kwrk>=mu+mv , mf>=mu*mv*idim ! tu(4) <= u(i-1) <= u(i) <= tu(nu-3), i=2,...,mu ! tv(4) <= v(j-1) <= v(j) <= tv(nv-3), j=2,...,mv ! ! other subroutines required: ! fpsuev,fpbspl ! ! references : ! de boor c : on calculating with b-splines, j. approximation theory 6 (1972) 50-62. ! cox m.g. : the numerical evaluation of b-splines, j. inst. maths applics 10 (1972) 134-149. ! dierckx p. : curve and surface fitting with splines, monographs on numerical analysis, ! oxford university press, 1993. ! ! author : ! p.dierckx ! dept. computer science, k.u.leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! ..scalar arguments.. integer, intent(in) :: idim,nu,nv,mu,mv,mf,lwrk,kwrk integer, intent(out) :: ier ! ..array arguments.. integer, intent(inout) :: iwrk(kwrk) real(RKIND), intent(in) :: tu(nu),tv(nv),c((nu-4)*(nv-4)*idim),u(mu),v(mv) real(RKIND), intent(inout) :: wrk(lwrk) real(RKIND), intent(out) :: f(mf) ! ..local scalars.. integer :: muv ! .. ! before starting computations a data check is made. if the input data ! are invalid control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR muv = mu+mv if (mf<mu*mv*idim .or. & lwrk<4*muv .or. & kwrk<muv .or. & mu<1 .or. & mv<1) return if (mu>1 .and. any(u(2:mu)<u(1:mu-1))) return if (mv>1 .and. any(v(2:mv)<v(1:mv-1))) return ! All checks passed ier = FITPACK_OK call fpsuev(idim,tu,nu,tv,nv,c,u,mu,v,mv,f,wrk(1),wrk(4*mu+1),iwrk(1),iwrk(mu+1)) end subroutine surev pure subroutine surfit(iopt,m,x,y,z,w,xb,xe,yb,ye,kx,ky,s,nxest,nyest,nmax,eps,nx,tx,ny,ty,& c,fp,wrk1,lwrk1,wrk2,lwrk2,iwrk,kwrk,ier) ! given the set of data points (x(i),y(i),z(i)) and the set of positive numbers w(i),i=1,...,m, ! subroutine surfit determines a smooth bivariate spline approximation s(x,y) of degrees kx and ! ky on the rect angle xb <= x <= xe, yb <= y <= ye. ! if iopt = -1 surfit calculates the weighted least-squares spline according to a given set of knots. ! if iopt >= 0 the total numbers nx and ny of these knots and their position tx(j),j=1,...,nx and ! ty(j),j=1,...,ny are chosen automatically by the routine. the smoothness of s(x,y) is then achieved ! by minimizing the discontinuity jumps in the derivatives of s(x,y) across the boundaries of the ! subpanels (tx(i),tx(i+1))*(ty(j),ty(j+1). ! The amounth of smoothness is determined by the condition that, for a given smoothing factor s>=0, ! f(p) = sum ((w(i)*(z(i)-s(x(i),y(i))))**2) be <= s. the fit is given in the b-spline representation ! (b-spline coefficients c((ny-ky-1)*(i-1)+j),i=1,...,nx-kx-1;j=1,...,ny-ky-1) and can be evaluated ! by means of subroutine bispev. ! ! calling sequence: ! call surfit(iopt,m,x,y,z,w,xb,xe,yb,ye,kx,ky,s,nxest,nyest, ! * nmax,eps,nx,tx,ny,ty,c,fp,wrk1,lwrk1,wrk2,lwrk2,iwrk,kwrk,ier) ! ! parameters: ! iopt : integer flag. on entry iopt must specify whether a weighted least-squares spline (iopt=-1) ! or a smoothing spline (iopt=0 or 1) must be determined. ! if iopt=0 the routine will start with an initial set of knots ! tx(i)=xb,tx(i+kx+1)=xe,i=1,...,kx+1;ty(i)=yb,ty(i+ky+1)=ye,i=1,...,ky+1. ! if iopt=1 the routine will continue with the set of knots found at the last call of the ! routine. attention: a call with iopt=1 must always be immediately preceded by another call ! with iopt=1 or iopt=0. unchanged on exit. ! m : integer. on entry m must specify the number of data points. ! m >= (kx+1)*(ky+1). unchanged on exit. ! x : real array of dimension at least (m). ! y : real array of dimension at least (m). ! z : real array of dimension at least (m). ! before entry, x(i),y(i),z(i) must be set to the co-ordinates of the i-th data point, for ! i=1,...,m. the order of the data points is immaterial. unchanged on exit. ! w : real array of dimension at least (m). before entry, w(i) must be set to the i-th value in ! the set of weights. the w(i) must be strictly positive. unchanged on exit. ! xb,xe : real values. on entry xb,xe,yb and ye must specify the boundaries of the rectangular ! yb,ye approximation domain. xb<=x(i)<=xe,yb<=y(i)<=ye,i=1,...,m. unchanged on exit. ! kx,ky : integer values. on entry kx and ky must specify the degrees of the spline. 1<=kx,ky<=5. it ! is recommended to use bicubic (kx=ky=3) splines. unchanged on exit. ! s : real. on entry (in case iopt>=0) s must specify the smoothing factor. s>=0. unchanged ! on exit. for advice on the choice of s see further comments ! nxest : integer. unchanged on exit. ! nyest : integer. unchanged on exit. ! on entry, nxest and nyest must specify an upper bound for the number of knots required in ! the x- and y-directions respect. these numbers will also determine the storage space needed ! by the routine. nxest >= 2*(kx+1), nyest >= 2*(ky+1). in most practical situation ! nxest = kx+1+sqrt(m/2), nyest = ky+1+sqrt(m/2) will be sufficient. see also further comments. ! nmax : integer. on entry nmax must specify the actual dimension of the arrays tx and ty. ! nmax >= nxest, nmax >=nyest. unchanged on exit. ! eps : real. on entry, eps must specify a threshold for determining the effective rank of an ! over-determined linear system of equations. 0 < eps < 1. if the number of decimal digits ! in the computer representation of a real number is q, then 10**(-q) ! is a suitable value for eps in most practical applications. unchanged on exit. ! nx : integer. unless ier=10 (in case iopt >=0), nx will contain the total number of knots with ! respect to the x-variable, of the spline approximation returned. if the computation mode ! iopt=1 is used, the value of nx should be left unchanged between subsequent calls. ! in case iopt=-1, the value of nx should be specified on entry. ! tx : real array of dimension nmax. ! on successful exit, this array will contain the knots of the spline with respect to the ! x-variable, i.e. the position of the interior knots tx(kx+2),...,tx(nx-kx-1) as well as the ! position of the additional knots tx(1)=...=tx(kx+1)=xb and tx(nx-kx)=...=tx(nx)=xe needed ! for the b-spline representation. if the computation mode iopt=1 is used, the values of tx(1), ! ...,tx(nx) should be left unchanged between subsequent calls. if the computation mode ! iopt=-1 is used, the values tx(kx+2),...tx(nx-kx-1) must be supplied by the user, before ! entry. see also the restrictions (ier=10). ! ny : integer. unless ier=10 (in case iopt >=0), ny will contain the total number of knots with ! respect to the y-variable, of the spline approximation returned. if the computation mode ! iopt=1 is used, the value of ny should be left unchanged between subsequent calls. ! in case iopt=-1, the value of ny should be specified on entry ! ty : real array of dimension nmax. on successful exit, this array will contain the knots of the ! spline with respect to the y-variable, i.e. the position of the interior knots ty(ky+2),..., ! ty(ny-ky-1) as well as the position of the additional knots ty(1)=...=ty(ky+1)=yb and ! ty(ny-ky)=...=ty(ny)=ye needed for the b-spline representation. if the computation mode ! iopt=1 is used, the values of ty(1),...,ty(ny) should be left unchanged between subsequent ! calls. if the computation mode iopt=-1 is used, the values ty(ky+2),...ty(ny-ky-1) must be ! supplied by the user, before entry. see also the restrictions (ier=10). ! c : real array of dimension at least (nxest-kx-1)*(nyest-ky-1). ! on successful exit, c contains the coefficients of the spline approximation s(x,y) ! fp : real. unless ier=10, fp contains the weighted sum of squared residuals of the spline ! approximation returned. ! wrk1 : real array of dimension (lwrk1). used as workspace. ! if the computation mode iopt=1 is used the value of wrk1(1) should be left unchanged between ! subsequent calls. on exit wrk1(2),wrk1(3),...,wrk1(1+(nx-kx-1)*(ny-ky-1)) will contain the ! values d(i)/max(d(i)),i=1,...,(nx-kx-1)*(ny-ky-1) with d(i) the i-th diagonal element of the ! reduced triangular matrix for calculating the b-spline coefficients. it includes those ! elements whose square is less than eps,which are treated as 0 in the case of presumed rank ! deficiency (ier<-2). ! lwrk1 : integer. on entry lwrk1 must specify the actual dimension of the array wrk1 as declared in ! the calling (sub)program. lwrk1 must not be too small. let ! u = nxest-kx-1, v = nyest-ky-1, km = max(kx,ky)+1, ! ne = max(nxest,nyest), bx = kx*v+ky+1, by = ky*u+kx+1, ! if(bx<=by) b1 = bx, b2 = b1+v-ky ! if(bx >by) b1 = by, b2 = b1+u-kx then ! lwrk1 >= u*v*(2+b1+b2)+2*(u+v+km*(m+ne)+ne-kx-ky)+b2+1 ! wrk2 : real array of dimension (lwrk2). used as workspace, but only in the case a rank deficient ! system is encountered. ! lwrk2 : integer. on entry lwrk2 must specify the actual dimension of the array wrk2 as declared in ! the calling (sub)program. lwrk2 > 0 . a save upper boundfor lwrk2 = u*v*(b2+1)+b2 where u,v ! and b2 are as above. if there are enough data points, scattered uniformly over the ! approximation domain and if the smoothing factor s is not too small, there is a good chance ! that this extra workspace is not needed. a lot of memory might therefore be saved by setting ! lwrk2=1. (see also ier > 10) ! iwrk : integer array of dimension (kwrk). used as workspace. ! kwrk : integer. on entry kwrk must specify the actual dimension of the array iwrk as declared in ! the calling (sub)program. kwrk >= m+(nxest-2*kx-1)*(nyest-2*ky-1). ! ier : integer. unless the routine detects an error, ier contains a non-positive value on exit, i.e. ! ier=0 : normal return. the spline returned has a residual sum of squares fp such that ! abs(fp-s)/s <= tol with tol a relative tolerance set to 0.001 by the program. ! ier=-1 : normal return. the spline returned is an interpolating spline (fp=0). ! ier=-2 : normal return. the spline returned is the weighted least-squares polynomial of degrees kx ! and ky. in this extreme case fp gives the upper bound for the smoothing factor s. ! ier<-2 : warning. the coefficients of the spline returned have been computed as the minimal norm ! least-squares solution of a (numerically) rank deficient system. (-ier) gives the rank. ! especially if the rank deficiency which can be computed as (nx-kx-1)*(ny-ky-1)+ier, is ! large the results may be inaccurate. they could also seriously depend on the value of eps. ! ier=1 : error. the required storage space exceeds the available storage space, as specified by the ! parameters nxest and nyest. ! probably causes : nxest or nyest too small. if these parameters are already large, it may ! also indicate that s is too small. the approximation returned is the weighted least-squares ! spline according to the current set of knots. parameter fp gives the corresponding weighted ! sum of squared residuals (fp>s). ! ier=2 : error. a theoretically impossible result was found during the iteration process for finding ! a smoothing spline with fp = s. probably causes : s too small or badly chosen eps. ! there is an approximation returned but the corresponding weighted sum of squared residuals ! does not satisfy the condition abs(fp-s)/s < tol. ! ier=3 : error. the maximal number of iterations maxit (set to 20 by the program) allowed for ! finding a smoothing spline with fp=s has been reached. probably causes : s too small there ! is an approximation returned but the corresponding weighted sum of squared residuals does ! not satisfy the condition abs(fp-s)/s < tol. ! ier=4 : error. no more knots can be added because the number of b-spline coefficients ! (nx-kx-1)*(ny-ky-1) already exceeds the number of data points m. likely causes: either s ! or m too small. the approximation returned is the weighted least-squares spline according ! to the current set of knots. the parameter fp gives the corresponding weighted sum of ! squared residuals (fp>s). ! ier=5 : error. no more knots can be added because the additional knot would (quasi) coincide with ! an old one. likely causes : s too small or too large a weight to an inaccurate data point. ! the approximation returned is the weighted least-squares spline according to the current ! set of knots. the parameter fp gives the corresponding weighted sum of squared residuals (fp>s). ! ier=10 : error. on entry, the input data are controlled on validity the following restrictions must ! be satisfied. ! -1<=iopt<=1, 1<=kx,ky<=5, m>=(kx+1)*(ky+1), nxest>=2*kx+2, ! nyest>=2*ky+2, 0<eps<1, nmax>=nxest, nmax>=nyest, ! xb<=x(i)<=xe, yb<=y(i)<=ye, w(i)>0, i=1,...,m ! lwrk1 >= u*v*(2+b1+b2)+2*(u+v+km*(m+ne)+ne-kx-ky)+b2+1 ! kwrk >= m+(nxest-2*kx-1)*(nyest-2*ky-1) ! if iopt=-1: 2*kx+2<=nx<=nxest ! xb<tx(kx+2)<tx(kx+3)<...<tx(nx-kx-1)<xe ! 2*ky+2<=ny<=nyest ! yb<ty(ky+2)<ty(ky+3)<...<ty(ny-ky-1)<ye ! if iopt>=0: s>=0 ! if one of these conditions is found to be violated,control is returned to the calling program. ! in that case there is no approximation returned. ! ier>10 : error. lwrk2 is too small, i.e. there is not enough work-space for computing the minimal ! least-squares solution of a rank deficient system of linear equations. ier gives the ! requested value for lwrk2. there is no approximation returned but, having saved the ! information contained in nx,ny,tx,ty,wrk1, and having adjusted the value of lwrk2 and ! the dimension of the array wrk2 accordingly, the user cancontinue at the point the program ! was left, by calling surfit with iopt=1. ! ! further comments: ! by means of the parameter s, the user can control the tradeoff between closeness of fit and smoothness ! of fit of the approximation. if s is too large, the spline will be too smooth and signal will be lost; ! if s is too small the spline will pick up too much noise. in the extreme cases the program will return ! an interpolating spline ifs=0 and the weighted least-squares polynomial (degrees kx,ky)if s is very ! large. between these extremes, a properly chosen s will result in a good compromise between closeness ! of fit and smoothness of fit. to decide whether an approximation, corresponding to a certain s is ! satisfactory the user is highly recommended to inspect the fits graphically. ! recommended values for s depend on the weights w(i). if these are taken as 1/d(i) with d(i) an estimate ! of the standard deviation of z(i), a good s-value should be found in the range (m-sqrt(2*m),m+sqrt(2*m)). ! if nothing is known about the statistical error in z(i) each w(i) can be set equal to one and s ! determined by trial and error, taking account of the comments above. the best is then to start with a ! very large value of s ( to determine the least-squares polynomial and the corresponding upper bound fp0 ! for s) and then to progressively decrease the value of s ( say by a factor 10 in the beginning, i.e. ! s=fp0/10, fp0/100,...and more carefully as the approximation shows more detail) to obtain closer fits. ! to choose s very small is strongly discouraged. this considerably increases computation time and memory ! requirements. it may also cause rank-deficiency (ier<-2) and endager numerical stability. to economize ! the search for a good s-value the program provides with different modes of computation. at the first ! call of the routine, or whenever he wants to restart with the initial set of knots the user must set ! iopt=0. ! if iopt=1 the program will continue with the set of knots found at the last call of the routine. this ! will save a lot of computation time if surfit is called repeatedly for different values of s. the number ! of knots of the spline returned and their location will depend on the value of s and on the complexity ! of the shape of the function underlying the data. if the computation mode iopt=1 is used, the knots ! returned may also depend on the s-values at previous calls (if these were smaller). therefore, if after ! a number of trials with different s-values and iopt=1, the user can finally accept a fit as satisfactory, ! it may be worthwhile for him to call surfit once more with the selected value for s but now with iopt=0. ! indeed, surfit may then return an approximation of the same quality of fit but with fewer knots and ! therefore better if data reduction is also an important objective for the user. the number of knots may ! also depend on the upper bounds nxest and nyest. indeed, if at a certain stage in surfit the number of ! knots in one direction (say nx) has reached the value of its upper bound (nxest), then from that moment ! on all subsequent knots are added in the other (y) direction. this may indicate that the value of nxest ! is too small. on the other hand, it gives the user the option of limiting the number of knots the ! routine locates in any direction for example, by setting nxest=2*kx+2 (the lowest allowable value for ! nxest), the user can indicate that he wants an approximation which is a simple polynomial of degree kx ! in the variable x. ! ! references: ! dierckx p. : an algorithm for surface fitting with spline functions ! ima j. numer. anal. 1 (1981) 267-283. ! dierckx p. : an algorithm for surface fitting with spline functions ! report tw50, dept. computer science,k.u.leuven, 1980. ! dierckx p. : curve and surface fitting with splines, monographs on ! numerical analysis, oxford university press, 1993. ! ! author: ! p.dierckx ! dept. computer science, k.u. leuven ! celestijnenlaan 200a, b-3001 heverlee, belgium. ! e-mail : Paul.Dierckx@cs.kuleuven.ac.be ! ! creation date : may 1979 ! ! .. ! ..scalar arguments.. real(RKIND), intent(in) :: xb,xe,yb,ye,s,eps real(RKIND), intent(inout) :: fp integer, intent(in) :: iopt,m,kx,ky,nxest,nyest,nmax,lwrk1,lwrk2,kwrk integer, intent(inout) :: nx,ny integer, intent(out) :: ier ! ..array arguments.. real(RKIND), intent(in) :: z(m),w(m) real(RKIND), intent(inout) :: x(m),y(m),tx(nmax),ty(nmax),c((nxest-kx-1)*(nyest-ky-1)),wrk1(lwrk1),& wrk2(lwrk2) integer, intent(inout) :: iwrk(kwrk) ! ..local scalars.. integer :: ib1,ib3,jb1,ki,kmax,km1,km2,kn,kwest,kx1,ky1,la,lbx,lby,lco,lf,lff,lfp,lh,lq,lsx,lsy, & lwest,ncest,nest,nek,nminx,nminy,nmx,nmy,nreg,nrint,nxk,nyk ! we set up the parameters tol and maxit. integer, parameter :: maxit = 20 real(RKIND), parameter :: tol = smallnum03 ! Size parameters kx1 = kx+1 ky1 = ky+1 kmax = max(kx,ky) km1 = kmax+1 km2 = km1+1 nminx = 2*kx1 nminy = 2*ky1 ! before starting computations a data check is made. if the input data ! are invalid,control is immediately repassed to the calling program. ier = FITPACK_INPUT_ERROR nest = max(nxest,nyest) nxk = nxest-kx1 nyk = nyest-ky1 ncest = nxk*nyk nmx = nxest-nminx+1 nmy = nyest-nminy+1 nrint = nmx+nmy nreg = nmx*nmy jb1 = ky*nxk+kx1 ib1 = min(kx*nyk+ky1,jb1) ib3 = kx1*nyk+1 lwest = ncest*(2+ib1+ib3)+2*(nrint+nest*km2+m*km1)+ib3 kwest = m+nreg if (.not.(eps>zero .and. eps<one)) return if (.not.(kx>0 .and. kx<=5)) return if (.not.(ky>0 .and. ky<=5)) return if (.not.(iopt>=(-1) .and. iopt<=1)) return if (.not.m>=(kx1*ky1)) return if (.not.(nxest>=nminx .and. nxest<=nmax)) return if (.not.(nyest>=nminy .and. nyest<=nmax)) return if (.not.(lwrk1>=lwest .and. kwrk>=kwest)) return if (.not.(xb<xe .and. yb<ye)) return if (any(w<=zero)) return if (any(x<xb .or. x>xe)) return if (any(y<xb .or. y>xe)) return if (iopt>=0) then if (s<zero) return else ! Check that the pre-existing x, y knot locations are monotonic if (nx<nminx .or. nx>nxest) return nxk = nx-kx1 tx(kx1) = xb tx(nxk+1) = xe if (any(tx(kx1+1:nxk+1)<=tx(kx1:nxk))) return if (ny<nminy .or. ny>nyest) return nyk = ny-ky1 ty(ky1) = yb ty(nyk+1) = ye if (any(ty(ky1+1:nyk+1)<=ty(ky1:nyk))) return endif ! All checks passed ier = FITPACK_OK ! we partition the working space and determine the spline approximation kn = 1 ki = kn+m lq = 2 la = lq+ncest*ib3 lf = la+ncest*ib1 lff = lf+ncest lfp = lff+ncest lco = lfp+nrint lh = lco+nrint lbx = lh+ib3 nek = nest*km2 lby = lbx+nek lsx = lby+nek lsy = lsx+m*km1 call fpsurf(iopt,m,x,y,z,w,xb,xe,yb,ye,kx,ky,s,nxest,nyest, & eps,tol,maxit,nest,km1,km2,ib1,ib3,ncest,nrint,nreg,nx,tx, & ny,ty,c,fp,wrk1(1),wrk1(lfp),wrk1(lco),wrk1(lf),wrk1(lff), & wrk1(la),wrk1(lq),wrk1(lbx),wrk1(lby),wrk1(lsx),wrk1(lsy), & wrk1(lh),iwrk(ki),iwrk(kn),wrk2,lwrk2,ier) return end subroutine surfit ! Swap two real numbers elemental subroutine swap_RKIND(a,b) real(RKIND), intent(inout) :: a,b real(RKIND) :: tmp tmp = a a = b b = tmp end subroutine swap_RKIND elemental subroutine swap_int(a,b) integer, intent(inout) :: a,b integer :: tmp tmp = a a = b b = tmp end subroutine swap_int ! Test if two reals have the same representation at the ! current precision elemental logical function equal(a,b) real(RKIND), intent(in) :: a,b equal = abs(a-b)<spacing(merge(a,b,abs(a)<abs(b))) end function equal elemental logical function not_equal(a,b) real(RKIND), intent(in) :: a,b not_equal = .not.equal(a,b) end function not_equal end module fitpack_core